Это было уже несколько раз спрашивали, но никогда на самом деле так точно, как ему о Google Fonts.
Так общие идеи, что:
- Чтобы нарисовать SVG на холсте, нам нужно загрузить его в
<img>
элемент первого.
- Из соображений безопасности
<img>
внутренние документы не могут выполнять внешние запросы.
Это означает, что вам необходимо встроить все свои внешние ресурсы в качестве dataURI внутри вашей собственной разметки svg, прежде чем загружать ее в элемент <img>
.
Так что для шрифта, вам необходимо добавить в <style>
элемент и заменить SRC шрифта между url(...)
, с версией dataURI.
Google шрифты вставлять документы, как тот, который вы используете, на самом деле являются только css-файлами, которые затем указывают на фактические файлы шрифтов. Поэтому нам нужно получить не только документ уровня первого уровня, но и фактические файлы шрифтов.
Вот аннотированный и работает (?) доказательство концепции, написанной с синтаксисом ES6, так что потребует современный браузер, но это может быть transpiled довольно легко, так как все методы в нем могут быть polyfiled ,
/*
Only tested on a really limited set of fonts, can very well not work
This should be taken as an proof of concept rather than a solid script.
\t
@Params : an url pointing to an embed Google Font stylesheet
@Returns : a Promise, fulfiled with all the cssRules converted to dataURI as an Array
*/
function GFontToDataURI(url) {
return fetch(url) // first fecth the embed stylesheet page
.then(resp => resp.text()) // we only need the text of it
.then(text => {
// now we need to parse the CSSruleSets contained
// but chrome doesn't support styleSheets in DOMParsed docs...
let s = document.createElement('style');
s.innerHTML = text;
document.head.appendChild(s);
let styleSheet = s.sheet
// this will help us to keep track of the rules and the original urls
let FontRule = rule => {
let src = rule.style.getPropertyValue('src') || rule.style.cssText.match(/url\(.*?\)/g)[0];
if (!src) return null;
let url = src.split('url(')[1].split(')')[0];
return {
rule: rule,
src: src,
url: url.replace(/\"/g, '')
};
};
let fontRules = [],
fontProms = [];
// iterate through all the cssRules of the embedded doc
// Edge doesn't make CSSRuleList enumerable...
for (let i = 0; i < styleSheet.cssRules.length; i++) {
let r = styleSheet.cssRules[i];
let fR = FontRule(r);
if (!fR) {
continue;
}
fontRules.push(fR);
fontProms.push(
fetch(fR.url) // fetch the actual font-file (.woff)
.then(resp => resp.blob())
.then(blob => {
return new Promise(resolve => {
// we have to return it as a dataURI
// because for whatever reason,
// browser are afraid of blobURI in <img> too...
let f = new FileReader();
f.onload = e => resolve(f.result);
f.readAsDataURL(blob);
})
})
.then(dataURL => {
// now that we have our dataURI version,
// we can replace the original URI with it
// and we return the full rule's cssText
return fR.rule.cssText.replace(fR.url, dataURL);
})
)
}
document.head.removeChild(s); // clean up
return Promise.all(fontProms); // wait for all this has been done
});
}
/* Demo Code */
const ctx = canvas.getContext('2d');
let svgData = '<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200">' +
'<foreignObject width="100%" height="100%">' +
'<div xmlns="http://www.w3.org/1999/xhtml" style="font-size:40px;font-family:Pangolin">' +
'test' +
'</div>' +
'</foreignObject>' +
'</svg>';
// I'll use a DOMParser because it's easier to do DOM manipulation for me
let svgDoc = new DOMParser().parseFromString(svgData, 'image/svg+xml');
// request our dataURI version
GFontToDataURI('https://fonts.googleapis.com/css?family=Pangolin')
.then(cssRules => { // we've got our array with all the cssRules
let svgNS = "http://www.w3.org/2000/svg";
// so let's append it in our svg node
let defs = svgDoc.createElementNS(svgNS, 'defs');
let style = svgDoc.createElementNS(svgNS, 'style');
style.innerHTML = cssRules.join('\n');
defs.appendChild(style);
svgDoc.documentElement.appendChild(defs);
// now we're good to create our string representation of the svg node
let str = new XMLSerializer().serializeToString(svgDoc.documentElement);
// Edge throws when blobURIs load dataURIs from https doc...
// So we'll use only dataURIs all the way...
let uri = 'data:image/svg+xml;charset=utf8,' + encodeURIComponent(str);
let img = new Image();
img.onload = function(e) {
URL.revokeObjectURL(this.src);
canvas.width = this.width;
canvas.height = this.height;
ctx.drawImage(this, 0, 0);
}
img.src = uri;
})
.catch(reason => console.log(reason)) // if something went wrong, it'll go here
<canvas id="canvas"></canvas>
Вы должны были бы включить шрифт в SVG разметкой в виде ссылки на URI данных. –
Я думаю, что это звучит как самое практичное решение, потому что после долгих исследований я могу подтвердить, что он не позволит мне загружать внешние файлы woff, необходимые для этого. Этот сайт выглядит лучше всего для начала: http://sosweetcreative.com/2613/font-face-and-base64-data-uri – supersan