Skip to content

Missing fonts in PNG exports #4885

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
cjacksudo opened this issue May 29, 2020 · 32 comments
Open

Missing fonts in PNG exports #4885

cjacksudo opened this issue May 29, 2020 · 32 comments
Assignees
Labels
bug something broken P3 backlog

Comments

@cjacksudo
Copy link

Here's an example of a plotly chart with a custom font included:

https://codepen.io/etpinard/pen/jAzVVL

When clicking the "download plot as PNG" option, the downloaded file appears to have a different font than the chart.

Is there something I need to do to make sure that the font is included with the exported image?

@archmoj
Copy link
Contributor

archmoj commented Jun 1, 2020

Hmm... not sure if this is a bug.
@antoinerg FYI - codepen somehow displays the custom font but it is not used in the PNG export.
However if I past the JS code in the dashboard, the custom font does not show up in the browser.

@alexcjohnson
Copy link
Collaborator

Certainly looks like a bug to me - thanks for the report @cjacksudo!

Did a little poking around - if you ask for an svg download (using config option toImageButtonOptions: {format: 'svg'}) the font is included correctly, so the problem is at the svg -> raster step.

My guess is the problem is with this canvas:

var canvas = document.createElement('canvas');

not being appended to the DOM, so it doesn't have access to fonts loaded in the document. Possibly just adding it to the DOM (but putting it offscreen somewhere) would fix the issue?

@alexcjohnson alexcjohnson added the bug something broken label Jun 1, 2020
@LTribelhorn
Copy link

Has there been any progress on this? Or is there a temporary work-around?

In my case the font isn't even included in svg downloads.

@antoinerg
Copy link
Contributor

I just tried the following Codepen and to my surprise, the PNG export does contain the correct font on Chromium Version 85.0.4183.83 (Developer Build) (64-bit) on Linux. I could however reproduce the issue on Firefox.

@antoinerg
Copy link
Contributor

antoinerg commented Nov 13, 2020

I forked the original Codepen provided in this issue (#4885 (comment)) and replaced 'Oswald' by Oswald and fonts are now properly rendered even in Firefox: https://codepen.io/antoinerg/pen/vYKbGKm

In summary, it seems like Firefox and some other browsers choke on single quotes. The library performs some manipulation of quotes in https://github.com/plotly/plotly.js/blob/master/src/snapshot/tosvg.js#L114. I suspect the fix will be in this file.

@antoinerg
Copy link
Contributor

antoinerg commented Nov 13, 2020

Has there been any progress on this? Or is there a temporary work-around?

In my case the font isn't even included in svg downloads.

@LTribelhorn are you using single quotes? Can you try removing them? See #4885 (comment) for an explanation.

@cjacksudo
Copy link
Author

@antoinerg for what it's worth, I tried your codepen without any luck - the behavior is the same, and the font is not included in the downloaded png.

I'm on chrome 86.0.4240.198

@antoinerg
Copy link
Contributor

antoinerg commented Nov 13, 2020

@antoinerg for what it's worth, I tried your codepen without any luck - the behavior is the same, and the font is not included in the downloaded png.

I'm on chrome 86.0.4240.198

Thanks @cjacksudo for the quick reply! What is your operating system?

Does this one also fail for you: https://codepen.io/antoinerg/pen/pobGgQZ ?

@cjacksudo
Copy link
Author

I'm on macOS (10.15.6).

Yeah, I'm seeing the same thing with https://codepen.io/antoinerg/pen/pobGgQZ

@antoinerg antoinerg self-assigned this Nov 13, 2020
@antoinerg
Copy link
Contributor

I updated my Codepen to use a dev build of plotly.js which contains a fix. Can someone please confirm it is indeed working on their OS/browser?

cc @archmoj @cjacksudo

@archmoj
Copy link
Contributor

archmoj commented Nov 13, 2020

I updated my Codepen to use a dev build of plotly.js which contains a fix. Can someone please confirm it is indeed working on their OS/browser?

cc @archmoj @cjacksudo

Not working on my machine.
@antoinerg
Wondering if one should open the downloaded image in the browser?

@antoinerg
Copy link
Contributor

Not working on my machine.

What's your browser and OS @archmoj ? It works for me both in FF and Chromium on Linux... 😕

Wondering if one should open the downloaded image in the browser?

Since they are PNG, it shouldn't matter what you use to open them!

@alexcjohnson
Copy link
Collaborator

That new codepen still fails for me (Chrome 86 or any other browser, Mac OS 11.0.1)

@archmoj
Copy link
Contributor

archmoj commented Nov 13, 2020

@antoinerg
Wondering if one should open the downloaded image in the browser?

Nevermind. It is a png so it should not matter.

@cjacksudo
Copy link
Author

Still fails for me as well

@antoinerg
Copy link
Contributor

antoinerg commented Nov 13, 2020

It turns out my Codepen was using fonts already installed on my workstation so please disregard my comments above 🤦‍♂️

The bad news is that we rely on <img> to turn our SVG into an image and according to https://stackoverflow.com/a/42405731

For security reasons, <img> inner documents can not make any external requests.
This means that you'll have to embed all your external resources as dataURIs inside your svg markup itself, before loading it to the <img> element.

Therefore, to support custom fonts in SVG/PNG exports via CSS (ultimately the @font-face rule), we would need to inject the style in the SVG itself inside <defs>. Example:

<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100">
    <defs>
        <style>
            @import url("https://fonts.googleapis.com/css?family=Roboto:400,400i,700,700i");
        </style>
    </defs>
    <style><![CDATA[svg text{stroke:none}]]></style>
    <text x="20" y="50" font-family="Roboto">Roboto</text>
</svg>

@alexcjohnson is this something we want to support (ie. allow users to specify fonts to embed via some attribute)?

@alexcjohnson
Copy link
Collaborator

It would be a bit unfortunate if we needed to require users to explicitly specify these, just to make the downloaded image match what's already in their graph; I was hoping we'd be able to just read out the@font-face rules that were active in the document, and add them automatically. And it seems like we can do that for locally-defined rules (by hunting through document.styleSheets) but it's forbidden for imported stylesheets... we can see the URLs and could just @import all of them?

That sounds like a pain though, and might cause problems in some contexts, so yeah perhaps in the short term we could just accept an option to specify these fonts explicitly.

@LTribelhorn
Copy link

LTribelhorn commented Nov 14, 2020

Has there been any progress on this? Or is there a temporary work-around?
In my case the font isn't even included in svg downloads.

@LTribelhorn are you using single quotes? Can you try removing them? See #4885 (comment) for an explanation.

@antoinerg Sorry for the late response and thank you very much for your effort.
I am using the R-Implementation of plotly so the usecase is different but I thought the problem might be connected. If you don't think so I could open a new issue in the R-Repository.

For context:
I am using plotly in a Shiny-app and I define the fonts as specified in the documentation with font_p <- list(family = "Roboto", size = 14) and import the font in the UI with tags$head(tags$style(HTML("@import url('//fonts.googleapis.com/css2?family=Roboto:wght@300;700&display=swap');")))

@cjacksudo
Copy link
Author

cjacksudo commented Dec 23, 2020

@antoinerg if the issue is downloading the font, shouldn't this pen work: https://codepen.io/cjacksudo/pen/yLazRRE?

I looked through the SO answer you linked (https://stackoverflow.com/a/42405731) and it seems like the setting the font-face rule to reference a data string should solve it, but it doesn't appear to...

EDIT: I see it - confirmed that adding the rule inside of the svg fixes it.

@Matteobikk90
Copy link

I still have this issue.

MacOS
"plotly.js": "^2.2.1",
"react-plotly.js": "^2.5.1",

@MarekSvob
Copy link

While this issue is being resolved, perhaps it should be noted (here and maybe in the documentation as well) that for the png exports to work properly, one should use fonts installed on the system.

On Mac OS, what worked for me was looking at what fonts were available in the Font Book.

@shivansingh9
Copy link

Do we have any fix for missing fonts in PNG exports? Any way by which we could override a custom font to image or append anything in the data url generated? @antoinerg

@cs-sebastian
Copy link

@antoinerg if the issue is downloading the font, shouldn't this pen work: https://codepen.io/cjacksudo/pen/yLazRRE?

I looked through the SO answer you linked (https://stackoverflow.com/a/42405731) and it seems like the setting the font-face rule to reference a data string should solve it, but it doesn't appear to...

EDIT: I see it - confirmed that adding the rule inside of the svg fixes it.

Hello.

I'm running into this exact issue. @antoinerg: Could you elaborate how you did this?

I tried adding custom styles with the fonts as data-urls (as mentioned above, maybe I did not get it right) to the svg, but this seems to be ignored by the toImage-function.

For different reasons I cannot include fonts hosted on a third party.

@wimbschimmelpfennig
Copy link

Is there a working workaround for this issue?

@WilliamMayor
Copy link

I just ran into this issue. For us the downloaded images had annotations with text that overflowed their containers. I think because the size of the container was calculated with the fonts loaded, but when you download the font aren't there, the text changes size, but the calculation isn't updated.

For example: https://ibb.co/fdtWWCL

I "fixed" it by making the plot always render with font-family: 'Open Sans', verdana, arial, sans-serif;, not ideal as this isn't our brand font, but at least it means the downloaded images work :)

I would be perfectly happy to provide some kind of config that told plotly which fonts to include in the SVG.

@ziyuang
Copy link

ziyuang commented Aug 11, 2023

I just ran into this issue. For us the downloaded images had annotations with text that overflowed their containers. I think because the size of the container was calculated with the fonts loaded, but when you download the font aren't there, the text changes size, but the calculation isn't updated.

For example: https://ibb.co/fdtWWCL

I "fixed" it by making the plot always render with font-family: 'Open Sans', verdana, arial, sans-serif;, not ideal as this isn't our brand font, but at least it means the downloaded images work :)

I would be perfectly happy to provide some kind of config that told plotly which fonts to include in the SVG.

The link is dead.

@WilliamMayor
Copy link

Ah yes, sorry about that, I assumed the image board would keep the image around for longer.

I could try to re-create the image if that's useful? It was a picture of an annotation where the text was too wide to fit inside its container.

@ziyuang
Copy link

ziyuang commented Aug 15, 2023

Ah yes, sorry about that, I assumed the image board would keep the image around for longer.

I could try to re-create the image if that's useful? It was a picture of an annotation where the text was too wide to fit inside its container.

No thanks :) I can imagine what it could have been.

@ziyuang
Copy link

ziyuang commented Aug 15, 2023

It turns out my Codepen was using fonts already installed on my workstation so please disregard my comments above 🤦‍♂️

The bad news is that we rely on <img> to turn our SVG into an image and according to https://stackoverflow.com/a/42405731

For security reasons, <img> inner documents can not make any external requests.
This means that you'll have to embed all your external resources as dataURIs inside your svg markup itself, before loading it to the <img> element.

Therefore, to support custom fonts in SVG/PNG exports via CSS (ultimately the @font-face rule), we would need to inject the style in the SVG itself inside <defs>. Example:

<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100">
    <defs>
        <style>
            @import url("https://fonts.googleapis.com/css?family=Roboto:400,400i,700,700i");
        </style>
    </defs>
    <style><![CDATA[svg text{stroke:none}]]></style>
    <text x="20" y="50" font-family="Roboto">Roboto</text>
</svg>

@alexcjohnson is this something we want to support (ie. allow users to specify fonts to embed via some attribute)?

How did you put the style inside defs? I have tried some DOM manipulation, but it seems Plotly.js recreates the plot from its data and layout, so the inserted style will not go to the downloaded image.

@baender
Copy link

baender commented Jun 20, 2024

Even though this is a bit of an old issue, I would want to ask about current developments (if there are any).

For now, I would be absolutely fine with using a workaround. However, as @ziyuang mentioned before, I don't really understand how I would need to manipulate the html, such that it exports the png with the according custom font.

Any assistance would be very much appreciated.

@gvwilson gvwilson added the P3 backlog label Aug 9, 2024
@jaboaf
Copy link

jaboaf commented Nov 20, 2024

Safari on macOS 10.14 Mojave or later prohibits using non-default system fonts.

  • You can make html render with the font if you load the font file explicitly with a file path or load it from somewhere on the internet, but the font in the png will fall back to a default.
  • if you change the download format to svg and open it in Safari, it will render with the fall back default.

with Firefox, if privacy and security is in Standard mode, the html will render with the local font and the png downloaded will reflect that font.

  • if privacy is in Strict mode, the font in the html and the downloaded png will be the fall back default
  • if privacy and security is in Custom mode and Suspected fingerprinters is checked and toggled to In all windows, the behavior will be the same as in Strict mode.
  • if privacy and security is in Custom mode and Suspected fingerprinters is checked and toggled to In private windows, the behavior will be the same as in Strict mode if you open in a private window and the behavior will be the same as in Standard mode if you open in a non-private window.

with Chrome, the html will render with the desired font if the html file is local and the pngs will reflect this, even if you select Don’t allow sites to use fonts installed on your device in Privacy and security>Fonts

svgs will reflect the behavior of html rendering based on the browser you open it with. the font styling defined in the plot is reflected in the style argument in certain tags within the svg. so, for example, if you try to use a local font in the html and are using safari and download an svg, if you open the svg in chrome it should render with the font you desired but if you open the svg in Safari it will not.

@hjdeheer
Copy link

hjdeheer commented Mar 3, 2025

Any update on this issue? I am trying font family = Monserrat, even installed it onto my system (Ubuntu 22), but png and SVG exports do not work. Looking for a workaround..

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug something broken P3 backlog
Projects
None yet
Development

No branches or pull requests