You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

node-canvas自定义Quote字体未生效问题求助

自定义Quote字体未在node-canvas中生效的排查与修复

问题概述

使用node-canvas生成图片时,注册的自定义Quote字体未应用到文本中,代码文件index.js与Quote.ttf位于同一根目录,核心代码如下:

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

const myfact = "when a person dies, they have 7 minutes of brain activity left."
const settings = {
  txtarea: {
    x: 540,
    y: 0,
    width: 864,
    height: 1080
  },
  font: 'Quote',
  lineHeight: 1,
  minFontSize: 50,
  maxFontSize: 70
};

async function generateImage() {
  const canvas = createCanvas(1080, 1080);
  const context = canvas.getContext('2d');

  const bgimage = await loadImage("./background.jpg");
  context.drawImage(bgimage, 0, 0, 1080, 1080);

  registerFont('./Quote.ttf', { family: 'Quote', Style: 'Regular' });
  
  context.textAlign = 'center';
  context.textBaseline = 'middle';
  context.fillStyle = '#fff';

  drawMultiLineFact(context, myfact, settings);

  const buffer = canvas.toBuffer('image/png');
  fs.writeFileSync('./image.png', buffer);
};

function drawMultiLineFact(context, myfact, settings) {
  const words = require('words-array')(myfact);
  var lines = [];
  let y;
  let lastFittingLines;
  let lastFittingFont;
  let lastFittingY;
  let lastFittingLineHeight;
  
  for (var fontSize = settings.minFontSize; fontSize <= settings.maxFontSize; fontSize++) {
    var lineHeight = fontSize * settings.lineHeight;
    context.font = `'` + fontSize + `px "Quote"'`
    var x = settings.txtarea.x;
    y = lineHeight;
    lines = []
    var line = ''

    for (var word of words) {
      var linePlus = line + word + ' '
      if (context.measureText(linePlus).width > (settings.txtarea.width)) {
        lines.push({ text: line, x: x, y: y });
        line = word + ' '
        y += lineHeight
      } else {
        line = linePlus
      };
    };
    lines.push({ text: line, x: x, y: y });
    if (y > settings.txtarea.height) break;
    lastFittingLines = lines;
    lastFittingFont = context.font;
    lastFittingY = y;
    lastFittingLineHeight = lineHeight;
  };
  lines = lastFittingLines;
  context.font = lastFittingFont;
  const offset = settings.txtarea.y - lastFittingLineHeight / 2 + (settings.txtarea.height - lastFittingY) / 2;
  for (var line of lines) {
    context.fillText(line.text.trim(), line.x, line.y + offset);
  };
};

generateImage();

排查出的关键问题

  • 字体注册时机错误:registerFont必须在createCanvas之前调用,canvas实例创建后注册字体无法被已创建的上下文识别。
  • 配置参数拼写错误:registerFont的配置项中,样式参数应为小写的style,而非大写的Style,参数名不匹配会导致字体样式注册失败。
  • font属性格式错误:设置context.font时的字符串存在多余单引号和嵌套错误,正确的字体格式应为fontSize + 'px Quote'或fontSize + 'px "Quote"',错误格式会导致canvas无法正确识别字体家族。

修复后的完整代码

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

const myfact = "when a person dies, they have 7 minutes of brain activity left."
const settings = {
  txtarea: {
    x: 540,
    y: 0,
    width: 864,
    height: 1080
  },
  font: 'Quote',
  lineHeight: 1,
  minFontSize: 50,
  maxFontSize: 70
};

// 提前注册字体,放在createCanvas之前
registerFont('./Quote.ttf', { family: 'Quote', style: 'Regular' });

async function generateImage() {
  const canvas = createCanvas(1080, 1080);
  const context = canvas.getContext('2d');

  const bgimage = await loadImage("./background.jpg");
  context.drawImage(bgimage, 0, 0, 1080, 1080);
  
  context.textAlign = 'center';
  context.textBaseline = 'middle';
  context.fillStyle = '#fff';

  drawMultiLineFact(context, myfact, settings);

  const buffer = canvas.toBuffer('image/png');
  fs.writeFileSync('./image.png', buffer);
};

function drawMultiLineFact(context, myfact, settings) {
  const words = require('words-array')(myfact);
  var lines = [];
  let y;
  let lastFittingLines;
  let lastFittingFont;
  let lastFittingY;
  let lastFittingLineHeight;
  
  for (var fontSize = settings.minFontSize; fontSize <= settings.maxFontSize; fontSize++) {
    var lineHeight = fontSize * settings.lineHeight;
    // 修正font属性的格式
    context.font = `${fontSize}px "Quote"`;
    var x = settings.txtarea.x;
    y = lineHeight;
    lines = []
    var line = ''

    for (var word of words) {
      var linePlus = line + word + ' '
      if (context.measureText(linePlus).width > (settings.txtarea.width)) {
        lines.push({ text: line, x: x, y: y });
        line = word + ' '
        y += lineHeight
      } else {
        line = linePlus
      };
    };
    lines.push({ text: line, x: x, y: y });
    if (y > settings.txtarea.height) break;
    lastFittingLines = lines;
    lastFittingFont = context.font;
    lastFittingY = y;
    lastFittingLineHeight = lineHeight;
  };
  lines = lastFittingLines;
  context.font = lastFittingFont;
  const offset = settings.txtarea.y - lastFittingLineHeight / 2 + (settings.txtarea.height - lastFittingY) / 2;
  for (var line of lines) {
    context.fillText(line.text.trim(), line.x, line.y + offset);
  };
};

generateImage();

修复说明

  1. 将registerFont移至createCanvas之前,确保字体在canvas实例创建时已完成注册。
  2. 修正registerFont配置中的Style为style,匹配官方API的参数名。
  3. 简化context.font的字符串格式,使用模板字符串生成正确的字体声明,避免多余引号导致的识别问题。

内容的提问来源于stack exchange,提问作者Satyam Kumar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 19:41:01