Image Processing in Node.js Using Jimp Library

Image Processing in Node.js Using Jimp Library

You are requested by a client to watermark an entire library of images, as well as cropping them to a consistent resolution. In fact, your solution needs to be integrated within the client’s existing Javascript infrastructure. This can be done with a simple yet capable (relatively new) package for NodeJS, named Jimp.

You are requested by a client to watermark an entire library of images, as well as cropping them to a consistent resolution. In fact, your solution needs to be integrated within the client’s existing Javascript infrastructure. This can be done with a simple yet capable (relatively new) package for NodeJS, named Jimp.

Jimp stands for JavaScript Image Manipulation Program, which is simply explained on it’s project page as “The “JavaScript Image Manipulation Program” :-)” — The project page can be found here:

jimp

Jimp adoption is on a growing trajectory, currently attracting over 180,000 weekly downloads at the time of this writing.

I found that they are quick to investigate and address issues on Github. Having found an issue myself on version 0.4.0 relating to text alignment, it was addressed within 24 hours and closed within 48 hours with a commit lined up for the next version.

The open issues right now are only in the 40s, which is quite impressive for such a large library that is also relatively new. There is clearly an active community here, therefore is a reliable bet to have under your npm belt.

Install the package into your project with the following command:

npm i jimp

Before jumping into Jimp development, make sure that it supports the image types you are working with — it is a bitmap manipulation library, so do not expect any SVG or vector based support here (this also means we need to convert popular font file formats (.otf, .ttf) into bitmap font files with the .fnt file format — more on this later).

Supported file types:

  • @jimp/jpeg
  • @jimp/png
  • @jimp/bmp
  • @jimp/tiff
  • @jimp/gif
Working with Images in NodeJS

Jimp can be imported directly into a serverside node script. We can adopt a promise based command flow that allows us to manipulate one thing at a time — build up our edits on our image and finally save our final image with the Jimp.write() function.

Using the Jimp library is actually a great way to practice your promises as each task can be broken down into a number of then() extensions, which we will soon see being demonstrated.

The way we will break down our image editing task is by doing the following:

  1. Reading a template / base image to work with in a raw directory.
  2. Clone it into a separate active image file that we know will be manipulated.
  3. Read the cloned image ready to make manipulations
  4. Load a watermark logo and place it onto the image
  5. Load a font file in order to bake text into the image
  6. Export the final image into an export directory.

Before exploring the script, let’s visit some considerations when working with image processing.

Consideration 1: Folder structure

With these kinds of tasks we need to make sure we do not overwrite original image files. For this reason, at a minimum, we should structure our project with at least 3 folders:

project_folder/
    raw/
       image1.jpg
       image2.jpg
       ...
   
    active/
    export/
    generate_image.js

A self explanatory but necessary procedure, separating raw images, active and completed exported image — just like you would not mix raw data with normalised feature data for a neural network in machine learning. The same principles apply here.

Consideration 2: External Image Libraries

Your client may not have their raw image files on their servers ready for you to manipulate — they may be on an external service, such as Dropbox, Google Drive or Amazon AWS. Well,this is not an issue — this actually saves us the task of separating raw files from our active and exported files. For completeness, here are the developer pages for those services:

Of course, you have the option to move your exported images to these services too. If you send the image byte data as you may do for Amazon S3, then your image file would not need to be publicly accessible over HTTP. But in the case you wish to copy an image from one URL to the other, then you will want your image to be accessed via an HTTP address, which leads me onto consideration 3.

Consideration 3: Public static folder with NodeJS Express

Express makes it extremely easy for us to set folders to be public. If I wanted to store all my exported images inside a static folder, I could firstly place it inside my root directory like so:

app/
   public
   static
   routes
   views
   app.js
   ...

And from here, edit my app.js file to create a route to this folder:

//development URL
http://localhost:3010/exported-images
//production URL
https://<your_domain>:3010/exported-images

You do not need to adhere to the name of the folder either — I can configure any URI I choose. Let’s say my server is running on port 3010; the URL to access this folder would be:

//development URL
http://localhost:3010/exported-images
//production URL
https://<your_domain>:3010/exported-images

As a final consideration, you may only want your images to be sitting inside a public folder as they are being copied to an external service, and to be deleted straight after the transfer takes place. Another consideration is to use a random string as the image name for added security.

Our Watermark and Text Jimp Script

With our considerations out of the way, let’s take a raw template and attach a logo and text onto it. The final image may resemble something like this: (note the centered watermark and copyright text at the bottom)

The Script

Let’s visit how our Jimp script looks in its full implementation before breaking it down:

var Jimp = require('jimp');

//if you are following along, create the following 2 images relative to this script:
let imgRaw = 'raw/image1.png'; //a 1024px x 1024px backgroound image
let imgLogo = 'raw/logo.png'; //a 155px x 72px logo
//---

let imgActive = 'active/image.jpg';
let imgExported = 'export/image1.jpg';

let textData = {
  text: '© JKRB Investments Limited', //the text to be rendered on the image
  maxWidth: 1004, //image width - 10px margin left - 10px margin right
  maxHeight: 72+20, //logo height + margin
  placementX: 10, // 10px in on the x axis
  placementY: 1024-(72+20)-10 //bottom of the image: height - maxHeight - margin 
};

//read template & clone raw image 
Jimp.read(imgRaw)
  .then(tpl => (tpl.clone().write(imgActive)))

  //read cloned (active) image
  .then(() => (Jimp.read(imgActive)))

  //combine logo into image
  .then(tpl => (
    Jimp.read(imgLogo).then(logoTpl => {
      logoTpl.opacity(0.2);
      return tpl.composite(logoTpl, 512-75, 512, [Jimp.BLEND_DESTINATION_OVER, 0.2, 0.2]);
    });
  )

  //load font	
  .then(tpl => (
    Jimp.loadFont(Jimp.FONT_SANS_32_WHITE).then(font => ([tpl, font]))
  ))
	
  //add footer text
  .then(data => {

    tpl = data[0];
    font = data[1];
  
    return tpl.print(font, textData.placementX, textData.placementY, {
      text: textData.text,
      alignmentX: Jimp.HORIZONTAL_ALIGN_CENTER,
      alignmentY: Jimp.VERTICAL_ALIGN_MIDDLE
    }, textData.maxWidth, textData.maxHeight);
  })

  //export image
  .then(tpl => (tpl.quality(100).write(imgExported)))

  //log exported filename
  .then(tpl => { 
    console.log('exported file: ' + imgExported);
  })

  //catch errors
  .catch(err => {
    console.error(err);
  });

jimp-example.js

I like the progressive nature of this script, with the simplicity of our then() workflow that makes the code easy to read and follow. And because of this, there probably is not too much documentation to follow on from this script — but let’s visit some areas that may be of interest to us fellow Javascript developers.

Defining variables

All script variables are defined at the top of the script so as to make it more readable and easier to update. Everything that follows our let variables is pure functionality.

Cloning Raw and Opening Active Image

Jimp.read(imgRaw)
  .then(tpl => (tpl.clone().write(imgActive))) 
  .then(() => (Jimp.read(imgActive)))

We use Jimp.read() to effectively “open” an image file to start manipulating it. Jimp.read() is a Promise, which returns the image object to work with, named tpl.

With tpl at hand, we call tpl.clone().write(), duplicating the raw image file we just opened and saving it in our active/ directory.

Combining Watermark Image

.then(tpl => (
   Jimp.read(imgLogo).then(logoTpl => {
      logoTpl.opacity(0.2);
      return tpl.composite(
         logoTpl, 
          512-75, 
          512, 
          [Jimp.BLEND_DESTINATION_OVER]);
   });  
)

Within the following then() block, we call Jimp.read() once more to load our logo watermark. The opacity of the logo is changed firstly with logoTpl.opacity(), which does not require a Promise!

Because of this we then move onto placing the logoTpl image into our main tpl image, with tpl.composite(). The parameters here are quite straight forward, passing the logo itself, its x and y positions, followed by a blend mode. Here we just need the logo to be placed over the image, Jimp.BLEND_DESTINATION_OVER.

Note: Take a look at the Jimp Basic Methods section of NPMJS to explore more about composite and the range of methods the package offers.

We return the result of tpl.composite() to move onto our text placement.

Loading our Text Font

.then(tpl => (
   Jimp.loadFont(Jimp.FONT_SANS_32_WHITE
   .then(font => ([tpl, font]))  
))

Here we are doing 2 things — firstly loading Jimps’ built-in size 32 white Sans font, allowing us to use it in any tpl.print() calls we make later to bake text into the image.

The second is extending this Promise and returning an array for use in the next then() block. You see, the next then() block needs our main tpl image object, and our loaded font object. Since Javascript does not support tuples as such, we can simply return an array of the required objects.

Note: Check out Jimp’s Writing Text documentation to see everything about loading bitmap fonts and printing them in your images. The font conversion tools at the end are needed to convert your fonts to bitmap .fnt files - Hiero in particular is very useful. Remember, you can only export one size - colour combination for each font, therefore it is likely you will need to load multiple fonts into your image processing scripts.

The image files themselves are commonly PNG files, where you can further mask or change the font opacity.

Printing Text on Our Template

.then(data => {
   tpl = data[0];
   font = data[1];
   return tpl.print(
      font,
      textData.placementX, 
      textData.placementY, 
      {      
         text: textData.text,      
         alignmentX: Jimp.HORIZONTAL_ALIGN_CENTER,      
         alignmentY: Jimp.VERTICAL_ALIGN_MIDDLE    
      }, 
      textData.maxWidth, 
      textData.maxHeight);  
})

Using our data array, we retreive the font and tpl objects, and call tpl.print() for adding text to our image.

Here the text is being added in the bottom center of the image. print takes our font object, x and y position, followed by an object defining the text itself with its alignments. maxWidth and maxHeight dictate how those alignments react in the image scene.

Exporting and Post Processing

.then(tpl => (tpl.quality(100).write(imgExported)))   
.then(tpl => {
   console.log('exported file: ' + imgExported);  
})
.catch(err => {
  console.error(err);
 });

With our last 2 then() blocks, we use tpl.quality().write() to export the image into our chosen export directory, and simply log that the process is finished. Handle any post processing here, including:

  • Resolving a Promise which this process may be hosted in
  • Returning the full URL of the image for an API response
  • Logging / storing this image record in a database
  • Delete your active file or uneeded leftovers (although in reality a supervisor script could be in charge of tidy-up operations)
  • Move onto another image processing job if more are queued up.

As you are a great Javascript programmer, the catch() clause will most likely not come in use — however let’s keep one there just in case! Handle any errors as you wish.

Where to go from here

This is just one use case with Jimp. Check out the full documentation on their NPMJS page if you are considering Jimp as your image processor of choice.

The project on Github can be found at https://github.com/oliver-moran/jimp.

How to get started Internationalization in JavaScript with NodeJS

How to get started Internationalization in JavaScript with NodeJS

Tutorial showing how to use the Intl JS API in NodeJS (i18n). We'll install a module to unlock the Intl API languages for Node and test out RelativeTimeFormat to translate and localise relative times in JavaScript.

Tutorial showing how to use the Intl JS API in NodeJS (i18n). We'll install a module to unlock the Intl API languages for Node and test out RelativeTimeFormat to translate and localise relative times in JavaScript. I'll tell you how to get started with the built-in internationalization library in JS for Node 12 and higher. We'll change the locale to see how the translation works and test different BCP 47 language tags.

Internationalization is a difficult undertaking but using the Intl API is an easy way to get started, it's great to see this new API in the JS language and available for use. Soon, you'll be able to have confidence using it in the browser as modern browsers support the major Intl features. Have a look at the browser compatibility charts to see which browsers and versions of node are supported.

Use Intl.RelativeTimeFormat for language-sensitive relative time formatting.
#javascript #nodejs #webdevelopment

MDN Documentation:

https://developer.mozilla.org/en-US/d...

Full ICU NPM package:

https://www.npmjs.com/package/full-icu

JavaScript Tutorial: if-else Statement in JavaScript

JavaScript Tutorial: if-else Statement in JavaScript

This JavaScript tutorial is a step by step guide on JavaScript If Else Statements. Learn how to use If Else in javascript and also JavaScript If Else Statements. if-else Statement in JavaScript. JavaScript's conditional statements: if; if-else; nested-if; if-else-if. These statements allow you to control the flow of your program's execution based upon conditions known only during run time.

Decision Making in programming is similar to decision making in real life. In programming also we face some situations where we want a certain block of code to be executed when some condition is fulfilled.
A programming language uses control statements to control the flow of execution of the program based on certain conditions. These are used to cause the flow of execution to advance and branch based on changes to the state of a program.

JavaScript’s conditional statements:

  • if
  • if-else
  • nested-if
  • if-else-if

These statements allow you to control the flow of your program’s execution based upon conditions known only during run time.

  • if: if statement is the most simple decision making statement. It is used to decide whether a certain statement or block of statements will be executed or not i.e if a certain condition is true then a block of statement is executed otherwise not.
    Syntax:
if(condition) 
{
   // Statements to execute if
   // condition is true
}

Here, condition after evaluation will be either true or false. if statement accepts boolean values – if the value is true then it will execute the block of statements under it.
If we do not provide the curly braces ‘{‘ and ‘}’ after if( condition ) then by default if statement will consider the immediate one statement to be inside its block. For example,

if(condition)
   statement1;
   statement2;

// Here if the condition is true, if block 
// will consider only statement1 to be inside 
// its block.

Flow chart:

Example:

<script type = "text/javaScript"> 

// JavaScript program to illustrate If statement 

var i = 10; 

if (i > 15) 
document.write("10 is less than 15"); 

// This statement will be executed 
// as if considers one statement by default 
document.write("I am Not in if"); 

< /script> 

Output:

I am Not in if
  • if-else: The if statement alone tells us that if a condition is true it will execute a block of statements and if the condition is false it won’t. But what if we want to do something else if the condition is false. Here comes the else statement. We can use the else statement with if statement to execute a block of code when the condition is false.
    Syntax:
if (condition)
{
    // Executes this block if
    // condition is true
}
else
{
    // Executes this block if
    // condition is false
}


Example:

<script type = "text/javaScript"> 

// JavaScript program to illustrate If-else statement 

var i = 10; 

if (i < 15) 
document.write("10 is less than 15"); 
else
document.write("I am Not in if"); 

< /script> 

Output:

i is smaller than 15
  • nested-if A nested if is an if statement that is the target of another if or else. Nested if statements means an if statement inside an if statement. Yes, JavaScript allows us to nest if statements within if statements. i.e, we can place an if statement inside another if statement.
    Syntax:
if (condition1) 
{
   // Executes when condition1 is true
   if (condition2) 
   {
      // Executes when condition2 is true
   }
}

Example:

<script type = "text/javaScript"> 

// JavaScript program to illustrate nested-if statement 

var i = 10; 

if (i == 10) { 

// First if statement 
if (i < 15) 
	document.write("i is smaller than 15"); 

// Nested - if statement 
// Will only be executed if statement above 
// it is true 
if (i < 12) 
	document.write("i is smaller than 12 too"); 
else
	document.write("i is greater than 15"); 
} 
< /script> 

Output:

i is smaller than 15
i is smaller than 12 too
  • if-else-if ladder Here, a user can decide among multiple options.The if statements are executed from the top down. As soon as one of the conditions controlling the if is true, the statement associated with that if is executed, and the rest of the ladder is bypassed. If none of the conditions is true, then the final else statement will be executed.
if (condition)
    statement;
else if (condition)
    statement;
.
.
else
    statement;


Example:

<script type = "text/javaScript"> 
// JavaScript program to illustrate nested-if statement 

var i = 20; 

if (i == 10) 
document.wrte("i is 10"); 
else if (i == 15) 
document.wrte("i is 15"); 
else if (i == 20) 
document.wrte("i is 20"); 
else
document.wrte("i is not present"); 
< /script> 

Output:

i is 20

How to Retrieve full Profile of LinkedIn User using Javascript

How to Retrieve full Profile of LinkedIn User using Javascript

I am trying to retrieve the full profile (especially job history and educational qualifications) of a linkedin user via the Javascript (Fetch LinkedIn Data Using JavaScript)

Here we are fetching LinkedIn data like Username, Email and other fields using JavaScript SDK.

Here we have 2 workarounds.

  1. Configuration of linkedIn developer api
  2. Javascript Code to fetch records

Configuration of linkedIn developer api

In order to fetch records, first we need to create developer api in linkedin which will act as token/identity while fetching data from other linkedin accounts.

So to create api, navigate to https://linkedin.com/developer/apps and click on 'Create Application'.

After navigating, fill in details like name, description and other required fields and then submit.

As we submit, it will create Client ID and Client Secret shown below, which we will be using in our code while communicating to fetch records from other LinkedIn account.

Note: We need to provide localhost Url here under Oauth 2.0. I am using my localhost, but you can probably use other production URLs under Oauth 2.0 where your app is configured. It will make your api  consider the Url as trusted which fetching records.

Javascript Code to fetch records

For getting user details like first name, last name,User image can be written as,

<script type="text/javascript" src="https://platform.linkedin.com/in.js">  
    api_key: XXXXXXX //Client ID  
    onLoad: OnLinkedInFrameworkLoad //Method that will be called on page load  
    authorize: true  
</script>  
<script type="text/javascript">  
    function OnLinkedInFrameworkLoad() {  
        IN.Event.on(IN, "auth", OnLinkedInAuth);  
    }  
  
    function OnLinkedInAuth() {  
        IN.API.Profile("me").result(ShowProfileData);  
    }  
  
    function ShowProfileData(profiles) {  
        var member = profiles.values[0];  
        var id = member.id;  
        var firstName = member.firstName;  
        var lastName = member.lastName;  
        var photo = member.pictureUrl;  
        var headline = member.headline;  
        //use information captured above  
        var stringToBind = "<p>First Name: " + firstName + " <p/><p> Last Name: " + lastName + "<p/><p>User ID: " + id + " and Head Line Provided: " + headline + "<p/>"  
        document.getElementById('profiles').innerHTML = stringToBind;  
    }  
</script>    

Kindly note we need to include 'https://platform.linkedin.com/in.js' as src under script type as it will act on this Javascript SDK provided by Linkedin.

In the same way we can also fetch records of any organization with the companyid as keyword.

<head>  
    <script type="text/javascript" src="https://platform.linkedin.com/in.js">  
        api_key: XXXXXXX ////Client ID  
        onLoad: onLinkedInLoad  
        authorize: true  
    </script>  
</head>  
  
<body>  
    <div id="displayUpdates"></div>  
    <script type="text/javascript">  
        function onLinkedInLoad() {  
            IN.Event.on(IN, "auth", onLinkedInAuth);  
            console.log("On auth");  
        }  
  
        function onLinkedInAuth() {  
            var cpnyID = XXXXX; //the Company ID for which we want updates  
            IN.API.Raw("/companies/" + cpnyID + "/updates?event-type=status-update&start=0&count=10&format=json").result(displayCompanyUpdates);  
            console.log("After auth");  
        }  
  
        function displayCompanyUpdates(result) {  
            var div = document.getElementById("displayUpdates");  
            var el = "<ul>";  
            var resValues = result.values;  
            for (var i in resValues) {  
                var share = resValues[i].updateContent.companyStatusUpdate.share;  
                var isContent = share.content;  
                var isTitled = isContent,  
                    isLinked = isContent,  
                    isDescription = isContent,  
                    isThumbnail = isContent,  
                    isComment = isContent;  
                if (isTitled) {  
                    var title = isContent.title;  
                } else {  
                    var title = "News headline";  
                }  
                var comment = share.comment;  
                if (isLinked) {  
                    var link = isContent.shortenedUrl;  
                } else {  
                    var link = "#";  
                }  
                if (isDescription) {  
                    var description = isContent.description;  
                } else {  
                    var description = "No description";  
                }  
                /* 
                if (isThumbnailz) { 
                var thumbnailUrl = isContent.thumbnailUrl; 
                } else { 
                var thumbnailUrl = "http://placehold.it/60x60"; 
                } 
                */  
                if (share) {  
                    var content = "<a target='_blank' href=" + link + ">" + comment + "</a><br>";  
                    //el += "<li><img src='" + thumbnailUrl + "' alt=''>" + content + "</li>";  
                    el += "<li><div>" + content + "</div></li>";  
                }  
                console.log(share);  
            }  
            el += "</ul>";  
            document.getElementById("displayUpdates").innerHTML = el;  
        }  
    </script>  
</body>  

We can get multiple metadata while fetching records for any any organization. We can get company updates as shown below.

Conclusion

We can also fetch any company specific data like company job updates/post, total likes, comments, and number of views along with a lot of metadata we can fetch which I have shown below.

Thank you for reading !