Search code examples
javascriptreactjscanvasnext.jsvercel

React with node canvas: registerFont causesTypeError: Object(...) is not a function


I'm making a word game with React and I need to draw the game on a canvas element then serve that as an image. It mostly works, except adding a font to the image.

More specifically, my game is built with React on next.js, and it works locally with just using the system fonts. But I also need it to work with server side rendering, and on the server the system fonts aren't installed. Which means it draws the image and text with little squares where the text should be.

My issue is with registerFont in the canvas package. The relevant part of my code is

import { registerFont, createCanvas } from 'canvas';
//import RobotoR from '../../public/Roboto/Roboto-Regular.ttf';

export const drawCanvas = ({ sentence, cards, workingCards, width, height }) => {
  // default canvas size
  let cw = 1200 // canvas width
  let ch = 630 // canvas height; this is a minimun, it might change

  ~~~

  registerFont('../../public/Roboto/Roboto-Regular.ttf', { family: 'Roboto' })
  const canvas = createCanvas(cw, ch)
  const ctx = canvas.getContext('2d')

The call to createCanvas works, the call to registerFont doesn't. I looked in node_modules/canvas and registerFont is exported slightly differently, but it seems to work for literally everyone else.

node_modules/canvas/index.js:

function createCanvas (width, height, type) {
  return new Canvas(width, height, type)
}

~


/**
 * Resolve paths for registerFont. Must be called *before* creating a Canvas
 * instance.
 * @param src {string} Path to font file.
 * @param fontFace {{family: string, weight?: string, style?: string}} Object
 * specifying font information. `weight` and `style` default to `"normal"`.
 */
function registerFont (src, fontFace) {
  // TODO this doesn't need to be on Canvas; it should just be a static method
  // of `bindings`.
  return Canvas._registerFont(fs.realpathSync(src), fontFace)
}

~

module.exports = {
  Canvas,
  ...
  registerFont,
  ...
  createCanvas,
  ...

I'm probably making some very stupid little mistake, but I can't find it. Help?


Solution

  • Answering myself in case anyone runs into the same issue. I asked someone who knows more than I do, and after puzzling through it together for a half hour he realized that node-canvas has a separate entry point if it's run in a browser. I did not know this was a thing, but it is.

    In the node-canvas package.json, there is a line, '"browser": "browser.js"', which tells it to export a limited set of functions when run in a browser.

    My drawing function is used in the browser and in a separate server side api process. But I don't need to register fonts in the browser, since there are already fonts installed, so I import "registerFont", then check if it comes back undefined. If it's defined, I use it, if not, I must be in a browser and don't need it.