(async () => {
const svgNS = "http://www.w3.org/2000/svg";
const svg = document.createElementNS( svgNS, "svg" );
const font_data = await fetchAsDataURL( "https://fonts.gstatic.com/s/inter/v2/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa1ZL7W0Q5nw.woff2" );
const style = document.createElementNS( svgNS, "style" );
style.textContent = `@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 200 900;
src: url(${ font_data }) format('woff2');
}`;
svg.append( style );
const foreignObject = document.createElementNS( svgNS, "foreignObject" );
foreignObject.setAttribute( "x", 0 );
foreignObject.setAttribute( "y", 0 );
const target = document.querySelector( ".target" );
const clone = cloneWithStyles( target );
foreignObject.append( clone );
const { width, height } = target.getBoundingClientRect();
foreignObject.setAttribute( "width", width );
foreignObject.setAttribute( "height", height );
svg.setAttribute( "width", width );
svg.setAttribute( "height", height );
svg.append( foreignObject );
const svg_markup = new XMLSerializer().serializeToString( svg );
const svg_file = new Blob( [ svg_markup ], { type: "image/svg+xml" } );
const img = new Image();
img.src = URL.createObjectURL( svg_file );
await img.decode();
URL.revokeObjectURL( img.src );
const canvas = document.createElement( "canvas" );
Object.assign( canvas, { width, height } );
const ctx = canvas.getContext( "2d" );
ctx.drawImage( img, 0, 0 );
document.body.append( canvas );
})().catch( console.error );
function fetchAsDataURL( url ) {
return fetch( url )
.then( (resp) => resp.ok && resp.blob() )
.then( (blob) => new Promise( (res) => {
const reader = new FileReader();
reader.onload = (evt) => res( reader.result );
reader.readAsDataURL( blob );
} )
);
}
function cloneWithStyles( source ) {
const clone = source.cloneNode( true );
const iframe = document.createElement( "iframe" );
document.body.append( iframe );
if( iframe.contentDocument ) {
iframe.contentDocument.body.append( clone );
}
const source_walker = document.createTreeWalker( source, NodeFilter.SHOW_ELEMENT, null );
const clone_walker = document.createTreeWalker( clone, NodeFilter.SHOW_ELEMENT, null );
let source_element = source_walker.currentNode;
let clone_element = clone_walker.currentNode;
while ( source_element ) {
const source_styles = getComputedStyle( source_element );
const clone_styles = getComputedStyle( clone_element );
const keys = (() => {
const props = new Set();
for( let prop in source_styles ) {
prop = prop.replace( /[A-Z]/g, (m) => "-" + m.toLowerCase() );
prop = prop.replace( /^webkit-/, "-webkit-" );
props.add( prop );
}
return props;
})();
for( let key of keys ) {
if( clone_styles[ key ] !== source_styles[ key ] ) {
clone_element.style.setProperty( key, source_styles[ key ] );
}
}
source_element = source_walker.nextNode()
clone_element = clone_walker.nextNode()
}
iframe.remove();
return clone;
}
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 200 900;
src: url(https://fonts.gstatic.com/s/inter/v2/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa1ZL7W0Q5nw.woff2) format('woff2');
}
.t1 {
font-family: 'Inter';
font-variation-settings: 'wght' 200;
}
.t2 {
font-family: 'Inter';
font-variation-settings: 'wght' 900;
}
canvas {
border: 1px solid;
}
<div class="target">
<span class="t1">
Hello
</span>
<span class="t2">
World
</span>
</div>