1---
  2title: Create placeholder images with sharp Node.js image processing library
  3url: create-placeholder-images-with-sharp.html
  4date: 2020-03-27T12:00:00+02:00
  5type: post
  6draft: false
  7---
  8
  9I have been searching for a solution to pre-generate some placeholder images for
 10image server I needed to develop that resizes images on S3. I though this would
 11be a 15min job and quickly found out how very mistaken I was.
 12
 13Even though Node.js is not really the best way to do this kind of things (surely
 14something written in C or Rust or even Golang would be the correct way to do
 15this but we didn't need the speed in our case) I found an excellent library
 16[sharp - High performance Node.js image
 17processing](https://github.com/lovell/sharp).
 18
 19Getting things running was a breeze.
 20
 21## Fetch image from S3 and save resized
 22
 23```js
 24const sharp = require('sharp');
 25const aws = require('aws-sdk');
 26
 27const x,y = 100;
 28const s3 = new aws.S3({});
 29
 30aws.config.update({
 31  secretAccessKey: 'secretAccessKey',
 32  accessKeyId: 'accessKeyId',
 33  region: 'region'
 34});
 35
 36const originalImage = await s3.getObject({
 37  Bucket: 'some-bucket-name',
 38  Key: 'image.jpg',
 39}).promise();
 40
 41const resizedImage = await sharp(originalImage.Body)
 42  .resize(x, y)
 43  .jpeg({ progressive: true })
 44  .toBuffer();
 45
 46s3.putObject({
 47  Bucket: 'some-bucket-name',
 48  Key: `optimized/${x}x${y}/image.jpg`,
 49  Body: resizedImage,
 50  ContentType: 'image/jpeg',
 51  ACL: 'public-read'
 52}).promise();
 53```
 54
 55All this code was wrapped inside a web service with some additional security 
 56checks and defensive coding to detect if key is missing on S3.
 57
 58And at that point I needed to return placeholder images as a response in case
 59key is missing or x,y are not allowed by the server etc. I could have created
 60PNG in Gimp and just serve them but I wanted to respect aspect ratio and I
 61didn't want to return some mangled images.
 62
 63> Main problem with finding a clean solution I could copy and paste and change a
 64> bit was a task. API is changing constantly and there weren't clear examples or
 65> I was unable to find them.
 66
 67## Generating placeholder images using SVG
 68
 69What I ended up was using SVG to generate text and created image with sharp and
 70used composition to combine both layers. Response returned by this function is a
 71buffer you can use to either upload to S3 or save to local file.
 72
 73```js
 74const generatePlaceholderImageWithText = async (width, height, message) => {
 75  const overlay = `<svg width="${width - 20}" height="${height - 20}">
 76    <text x="50%" y="50%" font-family="sans-serif" font-size="16" text-anchor="middle">${message}</text>
 77  </svg>`;
 78
 79  return await sharp({
 80    create: {
 81      width: width,
 82      height: height,
 83      channels: 4,
 84      background: { r: 230, g: 230, b: 230, alpha: 1 }
 85    }
 86  })
 87    .composite([{
 88      input: Buffer.from(overlay),
 89      gravity: 'center',
 90    }])
 91    .jpeg()
 92    .toBuffer();
 93}
 94```
 95
 96That is about it. Nothing more to it. You can change the color of the image by
 97changing `background` and if you want to change text styling you can adapt SVG
 98to your needs.
 99
100> Also be careful about the length of the text. This function positions text at
101> the center and adds `20px` padding on all sides. If text is longer than the
102> image it will get cut.