0

I am making a nodeJS script that places black text in a white box on top of my input background image. I want the white box to resize it's height based on how much text input. I made a wrapText function to hopefully resize but it always returns 1 thus making the box not resize properly. Here's my current code.

const { createCanvas, loadImage } = require('canvas');

// Background image path
const backgroundImagePath = 'bg2.jpg';

// Text properties
const text = 'Sample Text\nWith multiple lines\nOf varying length';
const textColor = 'black';
const font = 'Arial';
const fontSize = 50;
const maxWidth = 1500;

// Padding properties
const paddingRatio = 0.1; // 10% padding relative to font size
const minPadding = 10;

// Output image path
const outputImagePath = 'output.jpg';

// Create canvas and context
const canvas = createCanvas(1080, 1920);
const ctx = canvas.getContext('2d');

// Load background image
loadImage(backgroundImagePath).then(image => {
  // Draw background image
  ctx.drawImage(image, 0, 0, canvas.width, canvas.height);

  // Add white box with text in the middle
  const boxWidth = Math.min(maxWidth, 0.8 * canvas.width);
  const wrappedText = wrapText(ctx, text, boxWidth, fontSize);
  const lineHeight = Math.min((canvas.height - 2 * minPadding) / wrappedText.lines.length, fontSize * 1.5);
  const boxHeight = wrappedText.lines.length * lineHeight + 2 * minPadding;
  const x = canvas.width / 2 - boxWidth / 2;
  const y = canvas.height / 2 - boxHeight / 2;

  ctx.fillStyle = 'white';
  ctx.fillRect(x - minPadding, y - minPadding, boxWidth + 2 * minPadding, boxHeight);

  ctx.font = `${fontSize}px ${font}`;
  ctx.textAlign = 'center';
  ctx.textBaseline = 'middle';
  ctx.fillStyle = textColor;

  wrappedText.lines.forEach((line, i) => {
    ctx.fillText(line, x + boxWidth / 2, y + (i + 0.5) * lineHeight);
  });

  // Save output image
  const out = require('fs').createWriteStream(outputImagePath);
  const stream = canvas.createJPEGStream({ quality: 0.8 });
  stream.pipe(out);
  out.on('finish', () => console.log('Image created successfully'));
});

// Wrap text to fit inside a box with a given width
function wrapText(context, text, maxWidth, fontSize) {
  const words = text.split(' ');
  const lines = [];
  let line = '';
  let height = 0;
  let lineWidth = 0;
  const lineHeight = fontSize * 1.5;

  for (let n = 0; n < words.length; n++) {
    const metrics = context.measureText(words[n]);
    const testWidth = metrics.width;

    if (lineWidth + testWidth > maxWidth) {
      lines.push(line.trim());
      line = '';
      lineWidth = 0;
      height += lineHeight;
    }

    line += words[n] + ' ';
    lineWidth += testWidth;
  }

  if (line) {
    lines.push(line.trim());
    height += lineHeight;
  }

  return { lines, height };
}

inside my wrapText function I tried to rewrite it several times and I can't get the box to resize properly.

I want the box to always remain in the center and middle of the background image. And I want it to resize based on the amount of text is input.

I used the following to do that

const lineHeight = Math.min((canvas.height - 2 * minPadding) / wrappedText.lines.length, fontSize * 1.5);
const boxHeight = wrappedText.lines.length * lineHeight + 2 * minPadding;

Here's my wrappedText function specifically

// Wrap text to fit inside a box with a given width
function wrapText(context, text, maxWidth, fontSize) {
  const words = text.split(' ');
  const lines = [];
  let line = '';
  let height = 0;
  let lineWidth = 0;
  const lineHeight = fontSize * 1.5;

  for (let n = 0; n < words.length; n++) {
    const metrics = context.measureText(words[n]);
    const testWidth = metrics.width;

    if (lineWidth + testWidth > maxWidth) {
      lines.push(line.trim());
      line = '';
      lineWidth = 0;
      height += lineHeight;
    }

    line += words[n] + ' ';
    lineWidth += testWidth;
  }

  if (line) {
    lines.push(line.trim());
    height += lineHeight;
  }

  return { lines, height };
}
Josh Holly
  • 9
  • 1
  • 1
  • 5

0 Answers0