2017-02-22 88 views
1

В соответствии с документацией Mozilla вы можете нарисовать сложный HTML на холсте, как this.Как использовать шрифты Google в холсте при рисовании объектов DOM в SVG?

Что я не могу понять, это способ заставить Google шрифты работать с ним.

Смотрите этот пример ниже:

var canvas = document.getElementById('canvas'); 
 
    var ctx = canvas.getContext('2d'); 
 
    
 
    var data = '<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>'; 
 
    
 
    var DOMURL = window.URL || window.webkitURL || window; 
 
    
 
    var img = new Image(); 
 
    var svg = new Blob([data], {type: 'image/svg+xml;charset=utf-8'}); 
 
    var url = DOMURL.createObjectURL(svg); 
 
    
 
    img.onload = function() { 
 
     ctx.drawImage(img, 0, 0); 
 
     DOMURL.revokeObjectURL(url); 
 
    } 
 
    
 
    img.src = url;
<link href="https://fonts.googleapis.com/css?family=Pangolin" rel="stylesheet"> 
 

 
<div style="font-size:40px;font-family:Pangolin">test</div><hr> 
 
<canvas id="canvas" style="border:2px solid black;" width="200" height="200"></canvas>

+2

Вы должны были бы включить шрифт в SVG разметкой в ​​виде ссылки на URI данных. –

+0

Я думаю, что это звучит как самое практичное решение, потому что после долгих исследований я могу подтвердить, что он не позволит мне загружать внешние файлы woff, необходимые для этого. Этот сайт выглядит лучше всего для начала: http://sosweetcreative.com/2613/font-face-and-base64-data-uri – supersan

ответ

4

Это было уже несколько раз спрашивали, но никогда на самом деле так точно, как ему о 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>

+0

Я также сделал образец кода. http://jsdo.it/defghi1977/K53D Идея этого кода такая же, как у Kaiido's. – defghi1977

+0

Престижность в том, что он работает и объясняет это так хорошо! Это именно то, чем я был. Благодарю. – supersan

+0

Этот подход работает только для.файлы woff или также файлы .eot? – NiZa

-1

Первое, что вы можете попробовать это использовать Google web font loader , потому что вы генерации SVG до шрифта загружается браузером

, поэтому вам нужно убедиться, что загружены шрифты, а затем сгенерируйте svg/image

, если это не работает, вы можете создавать текстовые метки в вашем SVG и попробовать эти альтернативы для шрифтов https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/SVG_fonts