Automattic / node-canvas

Node canvas is a Cairo backed Canvas implementation for NodeJS.
10.19k stars 1.17k forks source link

Is register_font supposed to work for text inside an svg ? #1558

Open bgirschig opened 4 years ago

bgirschig commented 4 years ago

Issue or Feature

I would like to render an svg that contains text, images, etc... I have managed to make images work by parsing the svg and replacing the image urls with data-urls, but the same trick didn't work for svg.

I tried:

Is there a way to load a font so that it can be used while drawing an svg ?

Your Environment

canvas@2.6.1 node v12.14.0 os: debian

asturur commented 4 years ago

can you show an output svg?

bgirschig commented 4 years ago

sure!

Here is a stripped down version of what I'm trying to do:

const { createCanvas, registerFont, loadImage } = require("canvas");
const fs = require("fs");

// A simple svg, with a background and two texts
const svg = `
<svg width="600" height="600" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
  <rect x="10%" y="10%" width="80%" height="80%" fill="rgba(255,255,255,0.3)"/>
  <text x="50%" y="20%" text-anchor="middle" style="fill: white" font-family="Quicksand Light" font-size="60px" font-weight="normal">text 1</text>
  <text x="50%" y="30%" text-anchor="middle" style="fill: white" font-family="Brainster Free" font-size="60px" font-weight="normal">text 2</text>
</svg>
`

async function main() {
  // Load the font first, as mentionned in the doc (https://github.com/Automattic/node-canvas#registerfont)
  registerFont('./Brainster Free.otf', { family: "Brainster Free", weight: "normal" });

  // create the context
  const canvas = createCanvas(600, 600);
  const ctx = canvas.getContext('2d');

  // Draw the svg
  const buffer = Buffer.from(svg);
  const image = await loadImage(buffer);
  ctx.drawImage(image, 0, 0);

  // Draw text directly on canvas, to make sure the font is actually loaded
  ctx.textAlign = "center";
  ctx.fillStyle = "white";
  ctx.font = "60px Brainster Free";
  ctx.fillText("TEXT 3", 300, 320);

  saveCanvas(canvas, 'canvas_output.jpeg');
}

function saveCanvas(canvas, target) {
  const out = fs.createWriteStream(target);
  canvas.createJPEGStream().pipe(out);
  return new Promise((resolve)=>out.on('finish', resolve));
}

main();

And here is the result canvas_output

text 1 is draw using a font that is installed on my computer, to make sure that I'm specifying the font family correctly. I was hoping that text2 and text3 would be drawn with the same font, but they are not. Instead, text2 is drawn with what looks like the default fallback font.

I've also tried defining an @font-face in the svg itself but that didn't work either, and from what I understood from cairoSVG's documentation (which -i think- is what node-canvas uses for drawing SVGs) , it's not supposed to

asturur commented 4 years ago

I had the same problem and i did not manage to solve it. I wasn't sure from the issue description if you were trying to output an SVG or rasterizing it.

jeffkile commented 4 years ago

I ran into something similar and I ended up using svg2img as a workaround