plotly.js
plotly.js copied to clipboard
Missing fonts in PNG exports
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?
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.
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:
https://github.com/plotly/plotly.js/blob/5b8b1db1386b82db747cbad239fed754e624a18f/src/snapshot/toimage.js#L44
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?
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.
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.
I forked the original Codepen provided in this issue (https://github.com/plotly/plotly.js/issues/4885#issue-627594588) 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.
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 https://github.com/plotly/plotly.js/issues/4885#issuecomment-726852154 for an explanation.
@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 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 ?
I'm on macOS (10.15.6).
Yeah, I'm seeing the same thing with https://codepen.io/antoinerg/pen/pobGgQZ
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
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?
Not working on my machine.
What's your browser and OS @archmoj ? It works for me both in FF and Chromium on Linux... :confused:
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!
That new codepen still fails for me (Chrome 86 or any other browser, Mac OS 11.0.1)
@antoinerg Wondering if one should open the downloaded image in the browser?
Nevermind. It is a png
so it should not matter.
Still fails for me as well
It turns out my Codepen was using fonts already installed on my workstation so please disregard my comments above :man_facepalming:
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)?
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.
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');")))
~~@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.
I still have this issue.
MacOS "plotly.js": "^2.2.1", "react-plotly.js": "^2.5.1",
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.
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
~@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.
Is there a working workaround for this issue?
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.
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.
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.
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.
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/42405731For 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.