Skip to content
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

Rendering SVGs with custom fonts on macOS #2399

Closed
gfmio opened this issue Oct 5, 2020 · 15 comments
Closed

Rendering SVGs with custom fonts on macOS #2399

gfmio opened this issue Oct 5, 2020 · 15 comments

Comments

@gfmio
Copy link

gfmio commented Oct 5, 2020

What are you trying to achieve?

Render SVGs with custom fonts on macOS

Have you searched for similar questions?

Yes, and I've found the ones covering the usage of fonts.conf on Linux / in AWS Lambda.

I followed the same method and when running the code in a linux container, fontconfig gets updated correctly, logs the debug output and renders the custom fonts.

On macOS, the custom fonts do not get picked up, fontconfig does not give any debug output and the default fonts get rendered.

Curioiusly, on both macOS and Linux, the command shelljs command does list the custom fonts, so it seems to me that fontconfig does pick up the correct settings, but then the rendering step doesn't make use of the updated config.

Does sharp / librsvg not use fontconfig on macOS? Or is there something else I need to configure? If so, what?

Are you able to provide a minimal, standalone code sample that demonstrates this question?

import * as fs from "fs";
import * as path from "path";
import sharp from "sharp";
import * as shell from "shelljs";

const fontConfigTemplate = (fontPath: string) => `<?xml version="1.0"?>
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
<fontconfig>
  <dir>${fontPath}</dir>
  <cachedir>/tmp/fonts-cache/</cachedir>
  <config></config>
</fontconfig>
`;

const enableCustomFonts = () => {
  // Create the font config file
  const fontDir = path.resolve(path.join(__dirname, "assets", "fonts"));
  // console.log(fontDir);
  fs.writeFileSync(
    path.join(fontDir, "fonts.conf"),
    fontConfigTemplate(fontDir)
  );
  // Set the environment variable path
  process.env.FONTCONFIG_PATH = fontDir;
  // Set font config debugging to true
  process.env.FC_DEBUG = "1";
  console.log(shell.exec("fc-list").stdout);
};

export const main = async () => {
  enableCustomFonts();
  const svgPath = path.resolve(
    path.join(__dirname, "assets", "svgs", "MyCatIsGrumpy 2.svg")
  );

  // Render to image
  let image = sharp(svgPath, {
    density: 720
  });
  image = image.png();

  const outputBuffer = await image.toBuffer();

  fs.writeFileSync("out.png", outputBuffer);
};

main();

Are you able to provide a sample image that helps explain the question?

The test image I'm rendering is:

<svg viewBox="0 0 240 80" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
  <style type="text/css">
    .small { font: 13px; }
    .heavy { font: bold 30px; }
    .Rrrrr { font: 40px 'Mada'; fill: red; }
  </style>

  <rect x="120" y="10" width="50" height="50" rx="5" />
  <text x="20" y="35" class="small" style="font-family: 'Mada';">My</text>
  <text x="40" y="35" class="heavy" style="font-family: 'Decimal';">cat</text>
  <text x="55" y="55" class="small" style="font-family: 'Mercury Text G1';">is</text>
  <text x="65" y="55" class="Rrrrr" style="font-family: 'Brother-1816';">Grumpy!</text>
</svg>

The output on Linux:

out

The output on macOS:

out-macos

@gfmio gfmio added the question label Oct 5, 2020
@lovell
Copy link
Owner

lovell commented Oct 6, 2020

Have you tried setting the fontconfig-related environment variables before the call to require('sharp')?

The FC_DEBUG environment variable, e.g. FC_DEBUG=1024, might also help:
https://www.freedesktop.org/software/fontconfig/fontconfig-user.html#DEBUG

@gfmio
Copy link
Author

gfmio commented Oct 6, 2020

Yes, I've tried using FC_DEBUG=1024 and setting the environment variables manually before even starting the node process.

When I manually invoke FC_DEBUG=1024 FONTCONFIG_PATH=<PATH_TO_MY_CONFIG> FONTCONFIG_FILE=<PATH_TO_MY_CONFIG> fc-list, it uses the correct config files and lists only my custom fonts.

When invoking fc-list using shelljs from my node script after setting the environment variables, it also uses the correct config files and lists only my custom fonts.

But when rendering the SVG with sharp, it nevertheless doesn't use the custom fonts.

@lovell
Copy link
Owner

lovell commented Oct 6, 2020

The prebuilt binaries for macOS configure fontconfig with a default location of /usr/local/etc (so it will look for e.g. /usr/local/etc/fonts/fonts.conf), which is the same location as homebrew.

https://github.com/lovell/sharp-libvips/blob/master/build/lin.sh#L310

If you already have fontconfig installed via homebrew then you might need to run fc-cache to update its (shared) cache.

@gfmio
Copy link
Author

gfmio commented Oct 7, 2020

I still haven't been able to get this to work, but I'll try with a new minimal example from scratch this weekend.

@lovell
Copy link
Owner

lovell commented Oct 7, 2020

Thanks for the update, once you work it all out this is probably something to add to the docs.

@cleversprocket
Copy link

I'm having the same problem. I'm on macOS Catalina 10.15.7. I checked and I have fonts.conf in the path specified above. Running fc-list in terminal I see all my installed fonts. I ran fc-cache but it didn't help. Admittedly I don't know anything about fontconfig.

@cleversprocket
Copy link

I got it to work. I was using font-family: 'Suprema SemiBold'; which I verified is a valid font by viewing the SVG in the browser. I removed the second part of the font name SemiBold and sharp rendered the default Suprema font, which is Suprema Regular (or Suprema at 400 weight). I added font-weight: 600; to get the SemiBold font to render.

Here's a trimmed down example:

<svg xmlns="http://www.w3.org/2000/svg">
  <style>
    .myClass {
      font-family: 'Suprema';
      font-weight: 600;
    }
  </style>
  <text class="myClass">Font renders correctly</text>
</svg>

I wish I knew why this works. @gfmio hopefully this will fix it for you, too.

@kleisauke
Copy link
Contributor

If available, the pango-list and pango-view commands are also useful for debugging this. Sometimes the installed font could be named differently or is it necessary to separate the second part with a different delimiter. See for example:

$ pango-list | grep DIN
D-DIN Condensed 
  Regular:           D-DIN Condensed, Condensed
  DINCondensed-Bold: D-DIN Condensed, Bold Condensed
  *Italic:           D-DIN Condensed, Italic
  *Bold Italic:      D-DIN Condensed, Bold Italic
$ pango-view --font 'D-DIN Condensed Bold' --no-display --text 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' --output out.png 
$ pango-view --font 'D-DIN Condensed, Bold' --no-display --text 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' --output out2.png 

Output:
out
out2

@kleisauke
Copy link
Contributor

Does sharp / librsvg not use fontconfig on macOS?

Pango (the text renderer used by librsvg) defaults to the CoreText rendering and mapping backend on macOS. You could try to use the Fontconfig backend with the PANGOCAIRO_BACKEND="fontconfig" environment variable.

@lovell
Copy link
Owner

lovell commented Oct 22, 2020

Great sleuthing Kleis, I did not know this. Perhaps sharp should automagically set this env variable when using the prebuilt binaries?

@kleisauke
Copy link
Contributor

kleisauke commented Oct 23, 2020

That could work. Alternatively, we might be able to build Pango without CoreText support by removing this:
https://github.com/GNOME/pango/blob/c7e6f369079f60311cf241e8e05f4cd92f7c344b/meson.build#L371-L384

(Note that I'm not sure what the advantages of CoreText are compared to Fontconfig and vice versa)

@lovell
Copy link
Owner

lovell commented Oct 23, 2020

The env variable approach would allow the proposed new default setting of PANGOCAIRO_BACKEND="fontconfig" to be overridden back to CoreText via PANGOCAIRO_BACKEND="coretext" if required, so I'd be hesitant about removing this ability entirely.

@lovell lovell added this to the v0.27.0 milestone Nov 5, 2020
@lovell
Copy link
Owner

lovell commented Dec 20, 2020

Commit ef964b5 changes the default behaviour of pango to use fontconfig, which will be in v0.27.0.

If required, setting PANGOCAIRO_BACKEND="coretext" will restore the previous behaviour.

@lovell
Copy link
Owner

lovell commented Dec 22, 2020

sharp v0.27.0 now available, thanks for reporting this.

@lovell
Copy link
Owner

lovell commented Jan 13, 2021

sharp v0.27.1 will revert this change as it is causing too many problems - see #2515

If you require consistent cross-platform font behaviour, please continue to set PANGOCAIRO_BACKEND="fontconfig"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants