I'm trying to add a functionality to the app I'm building that lets the user to download a rendered SVG element as a PNG file. The app is written using React and I decided to use save-svg-to-png
package to achieve this. Everything works so far, but I have a problem with an element that uses a bold Roboto font - after downloading the image, the font falls back to the default (Times New Roman or something like that). Now, I know that among the options
object that can be passed to saveSvgToPng
function there is a fonts
parameter with a signature like so:
fonts - A list of {text, url, format} objects the specify what fonts to inline in the SVG. Omitting this option defaults to auto-detecting font rules.
But there are no usage examples to clarify that, neither I could find any example on the web. The problem I have seems to be with the url
property - I'm not sure what to include there to make the font render properly. I tried to generate a URI with base64 encoding of the font and adding the URL from the Google Fonts site (https://fonts.googleapis.com/css2?family=Roboto:wght@700&display=swap
), but none seem to work. Can anyone help me with figuring this out?
CodePudding user response:
You need to provide both the URI pointing to the actual font file (.woff) in the url
param, and the @font-face
rule in the text
param.
Note that the file your url points to is just a CSS file, the actual font files are still to be grasped from there (if you need a programmatic way, I wrote something some times ago you could probably reuse).
So in your case you'd have to pass (assuming you only have glyphs in the Latin range)
saveSvgAsPng(element, file_name, {
fonts: [
{
url: "https://fonts.gstatic.com/s/roboto/v27/KFOlCnqEu92Fr1MmWUlfBBc4AMP6lQ.woff2",
text: `
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 700;
font-display: swap;
src: url(https://fonts.gstatic.com/s/roboto/v27/KFOlCnqEu92Fr1MmWUlfBBc4AMP6lQ.woff2) format('woff2');
unicode-range: U 0000-00FF, U 0131, U 0152-0153, U 02BB-02BC, U 02C6, U 02DA, U 02DC, U 2000-206F, U 2074, U 20AC, U 2122, U 2191, U 2193, U 2212, U 2215, U FEFF, U FFFD;
}`
}
]
});
CodePudding user response:
I'm not so optimistic.
Using Fonts in SVG and
css - How do I use a custom font in an SVG image on my site? - Graphic Design Stack Exchange and
SVG text
and Small, Scalable, Accessible Typographic Designs | CSS-Tricks more or less concludes that it is not possible to display custom fonts in SVGs when they are displayed using <img>
. I know this is not answering your question but the following example shows how a inline SVG is loaded into an image object, drawn in <canvas>
and exported as a data URL to a PNG image. This is the underlying process for transforming a SVG to PNG and you can see that it breaks the custom font.
document.addEventListener('DOMContentLoaded', e => {
let img = document.images[0];
let svg = document.getElementById('svg');
let image = new Image();
let canvas = document.getElementById('canvas');
let ctx = canvas.getContext('2d');
image.addEventListener('load', e => {
ctx.drawImage(e.target, 0, 0, 300, 30);
img.src = canvas.toDataURL("image/png");
});
image.src = "data:image/svg xml," svg.outerHTML;
});
<p>SVG image:</p>
<svg id="svg" viewBox="0 0 100 10" width="300" height="30" xmlns="http://www.w3.org/2000/svg">
<style>
@import url('https://fonts.googleapis.com/css2?family=Roboto:wght@700&display=swap');
svg{
font-family: Roboto, sans-serif;
}
text {
font-family:'Roboto';
font-weight: bold;
}
</style>
<text font-size="10" text-anchor="middle" dominant-baseline="middle" x="50" y="5">Test</text>
</svg>
<p>Canvas image:</p>
<canvas id="canvas" width="300" height="30"></canvas>
<p>PNG image:</p>
<p><img /></p>
Update
OK, now more optimistic... I tested my example with the font as a data URL and it actually works. In the following example I have embedded the font as a data URL (unfortunately the font is too much data, so thee is just a placeholder).
This is the conclusion (and also Robert Longsons initial comment): The font should be embedded as a data URL.
document.addEventListener('DOMContentLoaded', e => {
let img = document.images[0];
let svg = document.getElementById('svg');
let image = new Image();
let canvas = document.getElementById('canvas');
let ctx = canvas.getContext('2d');
image.addEventListener('load', e => {
ctx.drawImage(e.target, 0, 0, 300, 30);
img.src = canvas.toDataURL("image/png");
});
image.src = "data:image/svg xml," svg.outerHTML;
});
<p>SVG image:</p>
<svg id="svg" viewBox="0 0 100 10" width="300" height="30" xmlns="http://www.w3.org/2000/svg">
<style>
<![CDATA[
@font-face {
font-family: Roboto;
src: url('data:font/ttf;base64,[ ---- font data goes here ------ ]') format("truetype");
}
svg{
font-family: Roboto, sans-serif;
}
text {
font-family: Roboto;
}
]]>
</style>
<text font-size="10" text-anchor="middle" dominant-baseline="middle" x="50" y="5">Test</text>
</svg>
<p>Canvas image:</p>
<canvas id="canvas" width="300" height="30"></canvas>
<p>PNG image:</p>
<p><img /></p>