Skip to content

GlobalFonts.register uses reference to passed-in Buffer which can be invalidated #1006

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
190n opened this issue Feb 24, 2025 · 2 comments · Fixed by #1014
Open

GlobalFonts.register uses reference to passed-in Buffer which can be invalidated #1006

190n opened this issue Feb 24, 2025 · 2 comments · Fixed by #1014
Assignees
Labels
bug Something isn't working

Comments

@190n
Copy link

190n commented Feb 24, 2025

The register function takes a Buffer of the font data, but it makes the registered font reference the memory owned by the Buffer instead of making a copy of the data. See:

auto typeface_data = SkData::MakeWithoutCopy(font, length);

This is fine sometimes, but it doesn't work if the buffer will get overwritten or if it gets garbage-collected. oven-sh/bun#17620 is an example of unexpected behavior caused by this issue.

Here is a script to demonstrate this problem:

import fs from 'node:fs/promises';
import { GlobalFonts, createCanvas } from "@napi-rs/canvas";

// `npm install @fontsource/roboto`, or replace with the path to another font
let roboto = Buffer.from(await fs.readFile('./node_modules/@fontsource/roboto/files/roboto-latin-400-normal.woff'));
GlobalFonts.register(
  roboto,
  "roboto"
);

if (process.env.OVERWRITE) {
  roboto.fill(0);
}

const canvas = createCanvas(420, 72);
const ctx = canvas.getContext("2d");
ctx.fillStyle = "silver";
ctx.beginPath();
ctx.rect(0, 0, 420, 72);
ctx.fill();
ctx.font = 'bold 72px "roboto"';
ctx.fillStyle = "#2B2B2B";
ctx.fillText("hello world", 0, 72);
const content = await canvas.encode("png");
await fs.writeFile("./out.png", content);

When run normally, out.png has the text "hello world" visible:

Image

When run with the environment variable OVERWRITE=1, the image is blank because the font data isn't valid anymore:

Image

I believe either GlobalFonts.register should make a copy of the Buffer data to ensure that it can always access the font data, or it should be clearly documented that the user needs to pass in a Buffer that will never be overwritten nor garbage-collected.

@190n
Copy link
Author

190n commented Mar 5, 2025

Thanks for the fix, but it looks like that might have only fixed the case where the buffer gets garbage-collected (I'm guessing based on the use of Arc), not the case where it is still alive but gets overwritten? After upgrading to v0.1.68, running the script I sent above with OVERWRITE=1 still produces a blank image with no text. I do see that GlobalFonts.register returns a FontKey object so it seems like I definitely have the right version installed.

@190n
Copy link
Author

190n commented Mar 7, 2025

Could you reopen this issue? Or should I open a new one? The reproducer script still shows the same buggy behavior for me in v0.1.68.

@Brooooooklyn Brooooooklyn reopened this Mar 8, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants