In ReactJS I have an interface that allows a user to make customizations to an SVG block. There are three components within the SVG: a rectangle (for background), text, and an SVG path image.
The SVG is rendered as such, with props coming from a parent component's state:
renderLogo() {
let style = this.props.logoStyle;
return (
<svg width="300" height="100" className={"logo"} xmlns="http://www.w3.org/2000/svg">
<g>
<rect id="background" x="0" y="0" width="300" height="100" rx={style.bgBorderRadius} ry={style.bgBorderRadius}
fill={style.backgroundColor} stroke="2" />
</g>
<svg
xmlns="http://www.w3.org/2000/svg"
width={style.iconSize}
height={style.iconSize}
viewBox="0 0 1000 1000"
x="10"
y="13"
id="svg4272">
<g>
{this.renderIcon()}
</g>
</svg>
<g>
<text
x="90"
y="58"
fontSize={style.fontSize}
fontFamily={style.fontFamily}
fontWeight={style.fontWeight}
stroke={style.fontStrokeColor}
strokeWidth={style.fontStrokeWidth}
fill={style.fontColor}
textAlign="right"
>{style.companyName}</text>
</g>
</svg>
);
}
render() {
let style = this.props.logoStyle;
return (
<div className="logo-preview">
{this.renderLogo()}
<canvas id="canvas" width="900" height="300"></canvas>
<button className="btn btn-success" onClick={this.onDownload}>Download</button>
</div>
);
}
The button triggers function onDownload() which then attempts to paint to the #canvas, and then launches a download of the PNG
onDownload() {
var svg = document.querySelector('svg.logo');
// This function is from here that I hoped would help with the issue: https://github.com/lukehorvat/computed-style-to-inline-style
var computedToInline = require('computed-style-to-inline-style');
computedToInline(svg, true);
var canvas = document.querySelector('canvas');
var ctx = canvas.getContext('2d');
var data = (new XMLSerializer()).serializeToString(svg);
var DOMURL = window.URL || window.webkitURL || window;
var img = new Image();
var svgBlob = new Blob([data], {type: 'image/svg+xml;charset=utf-8'});
var url = DOMURL.createObjectURL(svgBlob);
img.onload = () => {
// clip and scale up
ctx.drawImage(img, 0, 0, 300, 100, 0, 0, 900, 300);
DOMURL.revokeObjectURL(url);
var imgURI = canvas
.toDataURL('image/png')
.replace('image/png', 'image/octet-stream');
this.triggerDownload(imgURI);
};
img.src = url;
}
triggerDownload(imgURI) {
console.log("triggerDownload");
var evt = new MouseEvent('click', {
view: window,
bubbles: false,
cancelable: true
});
var a = document.createElement('a');
a.setAttribute('download', 'imageFile.png');
a.setAttribute('href', imgURI);
a.setAttribute('target','_blank');
a.dispatchEvent(evt);
}
The issue:
When drawing it to the canvas, the icon is rendering correctly, but the background (SVG component ) is removed, and the attributes (ex. font-family) are defaulting to Helvetica Neue instead of the selected fonts / attributes. This carries into the download file.
EDIT:
As mentioned one of the issues is the fonts, and these do come from Google. From the link provided in the comments, it seems I need to reference these via data URIs inline. I have encoded them using FontSquirrel.com, but I am unsure how to reference them in the SVG inline, in React:
@font-face {
font-family: 'pacificoregular';
src: url(data:application/font-woff2;charset=utf-8;base64,d09GMgA...),
url(data:application/font-woff;charset=utf-8;base64,d09Grg...) format('woff');
font-weight: normal;
font-style: normal;
}
I tried inserting this into an element in <defs><style>...</style></defs>
but it errors. I tried converting to React syntax like so, but it also fails:
<style>
@fontFace {
fontFamily: 'pacificoregular';
src: url(data:application/font-woff2;charset=utf-8;base64,d09GMgA...),
url(data:application/font-woff;charset=utf-8;base64,d09Grg...) format('woff');
fontWeight: normal;
fontStyle: normal;
}
</style>
How can I reference the font inline to the SVG if I cannot use @font-face? Or is there actually a proper way to utilize @font-face here?
EDIT 2:
Removed <def>
tags, and used @fontFace{}
syntax:
Error when attempting this syntax I receive a syntax error that is complaining about the ":" after 'fontFamily' in:
@fontFace {
fontFamily: 'pacificoregular';
...
}