Automattic / node-canvas

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

Fonts not working #2391

Open SalvoLunar opened 6 days ago

SalvoLunar commented 6 days ago

Hello there,

I've been trying to make custom fonts work in a lambda/.docker container for a week now but there seem to be no way to make it happen.

Here is my javascript code:

` import { createCanvas, loadImage, registerFont } from 'canvas'; import createDOMPurify from 'dompurify'; import { JSDOM } from 'jsdom';

async function generateCanvasImage(params) {

const config = params.templateName === 'book1' ? book1Config : book2Config; const imagesConfig = await fetchTemplate( https://xxxx.s3.eu-west-1.amazonaws.com/${params.templateName}.json );

registerFont('assets/fonts/bronic.ttf', { family: 'Bronic', weight: 'normal', style: 'regular' }); registerFont('assets/fonts/Neo-Regular.ttf', {family: 'Neo', weight: 'normal', style: 'regular'});

const scale = 3; // Increase for higher DPI; typical high-DPI screens use a scale of 2 or 3 const canvas = createCanvas( config.pageSize.width scale, config.pageSize.height scale ); const ctx = canvas.getContext('2d'); ctx.scale(scale, scale);

const pageIndex = params.pageIndex; const page = config.pages[pageIndex];

const pageCharacters = imagesConfig[pageIndex].characters;

//Draw background const bg = await loadImage(page.background); ctx.drawImage(bg, 0, 0, config.pageSize.width, config.pageSize.height);

// Draw characters for (let i = 0; i < page.characters.length; i++) { const character = page.characters[i]; const { size, position } = getCharacterSizeAndPosition(character, params); const id = slugify( character.id === 'pet' ? page-${pageIndex}-${params.petBreed}-${params.petColor} : page-${pageIndex}-${params.companionAge}-${params.companionGender}-${params.companionSkin}-${params.companionHair} ); logger.info(id, i);

if (
  typeof pageCharacters.filter((c) => c.id === character.id)[0] !==
  'undefined'
) {
  const characterImageObj = pageCharacters
    .filter((c) => c.id === character.id)[0]
    .prompts.filter((p) => p.id.toLowerCase() === id.toLowerCase())[0];
  if (
    typeof characterImageObj !== 'undefined' &&
    typeof characterImageObj.imgLocS3 !== 'undefined'
  ) {
    const characterImage = await fetchImage(
      characterImageObj.imgLocS3 + '?t=' + new Date().valueOf()
    );
    const img = await loadImage(
      `data:image/jpeg;base64,${Buffer.from(characterImage).toString(
        'base64'
      )}`
    );
    ctx.drawImage(img, position.x, position.y, size, size);
    // doc.image(Buffer.from(characterImage), position.x, position.y, { fit: [size, size], align: 'center', valign: 'center' });
  } else {
    logger.error(`Found undefined for ${id}`);
  }
} else {
  logger.error(`Found undefined character for ${id}`);
}

}

for (let o = 0; o < page.overlays.length; o++) { let overlay = page.overlays[o]; console.log(overlay); const img = await loadImage(overlay.url); ctx.drawImage( img, overlay.position.x, overlay.position.y, config.pageSize.width, config.pageSize.height ); }

for (let t = 0; t < page.text_contents.length; t++) { let text_content = page.text_contents[t];

ctx.fillStyle = '#D9FFF2';
console.log(`'${text_content.fontSize}px "${text_content.font}"'`);
ctx.font = `${text_content.fontSize}px "${text_content.font}"`;
ctx.textAlign = text_content.align;

ctx.fillText(
  replacePlaceholders(text_content.text, params),
  text_content.position.x,
  text_content.position.y
);

}

return canvas.toBuffer(); }`

These are my system details

"node version" 20 "node-canvas":"2.11.2", "cairoVersion":"1.17.6", "jpegVersion":"6b", "gifVersion":"5.2.1", "freetypeVersion":"2.13.0", "pangoVersion":"1.48.10"

The whole application runs inside a lambda / docker container, this is the dockerfile

`FROM public.ecr.aws/lambda/nodejs:20

RUN dnf install -y gcc-c++ \ cairo \ cairo-devel \ pango \ pango-devel \ libjpeg \ libjpeg-devel \ giflib \ giflib-devel \ libpng \ libpng-devel \ pixman \ pixman-devel \ python3 \ make

ENV PKG_CONFIG_PATH=/usr/lib64/pkgconfig:/usr/lib/pkgconfig:/usr/share/pkgconfig

RUN rm -rf /usr/share/fonts/ RUN rm -rf /usr/local/share/fonts/ RUN rm -rf ${HOME}/.local/share/fonts/ RUN mkdir -p /usr/local/share/fonts/bronic COPY assets/fonts/bronic.ttf /usr/local/share/fonts/bronic RUN chmod 644 /usr/local/share/fonts/bronic/ RUN mkdir -p /usr/local/share/fonts/allianz-neo COPY assets/fonts/Neo-Regular.ttf /usr/local/share/fonts/neo RUN chmod 644 /usr/local/share/fonts/allianz-neo/*

ENV FONTCONFIG_PATH /usr/local/share/fonts/ RUN fc-cache --really-force --verbose COPY assets ${LAMBDA_TASK_ROOT}/assets COPY templates ${LAMBDA_TASK_ROOT}/templates RUN ls -la ${LAMBDA_TASK_ROOT}/ COPY package.json handler.js pageGenerator.js sizingLogic.js ${LAMBDA_TASK_ROOT}

WORKDIR ${LAMBDA_TASK_ROOT}

RUN chmod 777 ${LAMBDA_TASK_ROOT}/assets/fonts

RUN npm i node-gyp -g

RUN npm install

CMD ["handler.generatePages"]`

I've tried both the approaches installing the font directly onto docker and then just referencing it into my code, or using registerFont in the code and pointing to the correct ttf file.

Nothing seems to be working and there seems to be no utility to debug this, to verify if the fonts have been loaded correctly.

I can definitely see that the font file is found but then it just doesn't render.

Does anybody have any idea of how to solve this issue?

Thanks, Salvo

SalvoLunar commented 6 days ago

Nevermind.!!!.... I solved this issue adding:

RUN npm rebuild canvas --build-from-source

to my dockerfile!!