Create social media image programmatically - Part 2

The first part, we covered the

Welcome to the second part of Create social media image programmatically. The first part, we covered how to create and save an social media image with Node.js and Canvas.

In this article we will write a function that wrap and position based on its length.

First let's stub the wrapText function:

function wrapText(...) {

}

Next let's figure it out where we would call the wrapText function inside createPageCover function:

async function createPageCover(title, slug) {
  ...
  // Add post title at the top of the image
  context.font = 'bold 60pt Roboto'
  context.textAlign = 'center'
  context.textBaseline = 'top'
  context.fillStyle = '#fff'
-  context.fillText(title, 600, 215)
+  wrapText(...)
  ...
}

Here is the magic sauce: we will split the text into words and loop through them and measure their total width until it goes out of the canva context size, then split those in lines:

function wrapText(context, text, x, y, maxWidth, lineHeight) {

  const words = text.split(' ')
  const lines = []
  let line = ''

  for (let i = 0; i < words.length; i++) {
    let test = words[i]
    let metrics = context.measureText(test);

    while (metrics.width > maxWidth) {
      // Determine how much of the word will fit
      test = test.substring(0, test.length - 1);
      metrics = context.measureText(test);
    }

    if (words[i] != test) {
      words.splice(i + 1, 0,  words[i].substr(test.length))
      words[i] = test;
    }

    test = line + words[i] + ' ';
    metrics = context.measureText(test);

    if (metrics.width > maxWidth && i > 0) {
      lines.push({line, x, y})
      line = words[i] + ' '
      y += lineHeight
    } else {
      line = test;
    }
  }

  lines.push({line, x, y})

  let negativeTop = 0

  if (lines.length > 1) {
    negativeTop = lines.length * 25
  }

  lines.forEach( ln => {
    context.fillText(ln.line, ln.x, ln.y - negativeTop)
  });
}

Finally we just update the function invocation:

async function createPageCover(title, slug) {
  ...
  // Add post title at the top of the image
  context.font = 'bold 60pt Roboto'
  context.textAlign = 'center'
  context.textBaseline = 'top'
  context.fillStyle = '#fff'
-  wrapText(...)
+  wrapText(context, title, 600, 215, 1000, 100)
  ...
}

That's it! Now gatsby will generate social media cover picture dynamically based on the title of the articles.