204 lines
5.2 KiB
JavaScript
204 lines
5.2 KiB
JavaScript
|
var Class = require('../../core/class');
|
||
|
var Path = require('../../core/path');
|
||
|
var SVGParser = require('./core');
|
||
|
var Font = require('../../shapes/font');
|
||
|
|
||
|
var parse = SVGParser.prototype.parse;
|
||
|
|
||
|
var matchFontURL = /url\(['"\s]*([^\)]*?)['"\s]*\)\s+format\(['"]?svg['"]?\)/i;
|
||
|
|
||
|
var trimFontFamily = /^['"\s]+|['"\s]+$|,.*$/g;
|
||
|
|
||
|
var fillFaceAttributes = function(element, face){
|
||
|
var attributes = element.attributes;
|
||
|
for (var i = 0, l = attributes.length; i < l; i++){
|
||
|
var attribute = attributes[i];
|
||
|
if (!(attribute.nodeName in face)){
|
||
|
face[attribute.nodeName] = attribute.nodeValue;
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
|
||
|
SVGParser.implement({
|
||
|
|
||
|
parse: function(element, styles){
|
||
|
if (element.documentElement && element != this.fontDocument){
|
||
|
this.fontDocument = element;
|
||
|
this.findFonts(element);
|
||
|
}
|
||
|
return parse.apply(this, arguments);
|
||
|
},
|
||
|
|
||
|
findFonts: function(document){
|
||
|
var fonts = this.fonts || (this.fonts = {});
|
||
|
var root = document.documentElement, node = root;
|
||
|
treewalker: while (node){
|
||
|
if (node.nodeType == 1 && node.nodeName == 'font-face'){
|
||
|
this.fontFace(node);
|
||
|
}
|
||
|
if (node.firstChild){
|
||
|
node = node.firstChild;
|
||
|
} else {
|
||
|
while (!node.nextSibling){
|
||
|
node = node.parentNode;
|
||
|
if (!node || node == root) break treewalker;
|
||
|
}
|
||
|
node = node.nextSibling;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (this.findCSS){
|
||
|
var rules = this.findCSS(document).cssRules;
|
||
|
for (var i = 0, l = rules.length; i < l; i++){
|
||
|
var rule = rules[i];
|
||
|
if (rule.fontFace){
|
||
|
var url = matchFontURL.exec(rule.style.src);
|
||
|
if (url) this.fontURL(document, url[1], rule.style);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
|
||
|
fontFace: function(element){
|
||
|
var face = {};
|
||
|
fillFaceAttributes(element, face);
|
||
|
if (element.parentNode.nodeName == 'font') this.font(element.parentNode, face);
|
||
|
var node = element.firstChild;
|
||
|
while (node){
|
||
|
if (node.nodeName == 'font-face-src'){
|
||
|
node = node.firstChild;
|
||
|
continue;
|
||
|
}
|
||
|
if (node.nodeName == 'font-face-uri'){
|
||
|
var url = node.getAttribute('xlink:href');
|
||
|
this.fontURL(element.ownerDocument, url, face);
|
||
|
}
|
||
|
node = node.nextSibling;
|
||
|
}
|
||
|
},
|
||
|
|
||
|
fontURL: function(document, url, face){
|
||
|
if (!face['font-family']) return;
|
||
|
var pendingFonts = this.pendingFonts || (this.pendingFonts = {});
|
||
|
var family = face['font-family'].replace(trimFontFamily, '');
|
||
|
var callbacks = pendingFonts[family] = [];
|
||
|
this.findByURL(document, url, function(font){
|
||
|
delete pendingFonts[family];
|
||
|
if (font){
|
||
|
var f = this.font(font, face);
|
||
|
for (var i = 0, l = callbacks.length; i < l; i++)
|
||
|
callbacks[i].call(this, f);
|
||
|
}
|
||
|
});
|
||
|
},
|
||
|
|
||
|
findFont: function(styles, callback){
|
||
|
var family = styles['font-family'].replace(trimFontFamily, '');
|
||
|
var callbacks = this.pendingFonts && this.pendingFonts[family];
|
||
|
if (callbacks){
|
||
|
callbacks.push(callback);
|
||
|
} else if (this.fonts){
|
||
|
callback.call(this, this.fonts[family]);
|
||
|
} else {
|
||
|
callback.call(this, null);
|
||
|
}
|
||
|
},
|
||
|
|
||
|
font: function(element, face){
|
||
|
var glyphs = {}, font = { face: face, glyphs: glyphs };
|
||
|
|
||
|
var w = element.getAttribute('horiz-adv-x');
|
||
|
if (w) font.w = parseInt(w, 10);
|
||
|
|
||
|
var node = element.firstChild;
|
||
|
while (node){
|
||
|
switch(node.nodeName){
|
||
|
case 'font-face':
|
||
|
fillFaceAttributes(node, face);
|
||
|
break;
|
||
|
case 'missing-glyph':
|
||
|
case 'glyph':
|
||
|
var glyph = {},
|
||
|
code = node.nodeName == 'missing-glyph' ? 'missing' : node.getAttribute('unicode'),
|
||
|
w = node.getAttribute('horiz-adv-x'),
|
||
|
d = node.getAttribute('d');
|
||
|
if (!code) break;
|
||
|
if (w) glyph.w = parseInt(w, 10);
|
||
|
|
||
|
if (d){
|
||
|
// Flip path
|
||
|
var path = new FlippedVMLPathAtNormalPrecision(d);
|
||
|
glyph.d = path.toVML().substr(1);
|
||
|
}
|
||
|
glyphs[code] = glyph;
|
||
|
break;
|
||
|
// TODO: Kerning
|
||
|
}
|
||
|
node = node.nextSibling;
|
||
|
}
|
||
|
|
||
|
var units = face['units-per-em'];
|
||
|
|
||
|
if (isNaN(units)) face['units-per-em'] = units = 1000;
|
||
|
|
||
|
if (face.ascent == null) face.ascent = face.descent == null ? 0.8 * units : units - face.descent;
|
||
|
if (face.descent == null) face.descent = face.ascent - units;
|
||
|
|
||
|
var family = face['font-family'];
|
||
|
if (!family) return;
|
||
|
face['font-family'] = family = family.replace(trimFontFamily, '');
|
||
|
|
||
|
var fonts = this.fonts || (this.fonts = {});
|
||
|
if (face.ascent) face.ascent = +face.ascent;
|
||
|
if (face.descent) face.descent = +face.descent;
|
||
|
fonts[family] = font;
|
||
|
if (this.MODE.Font)
|
||
|
this.MODE.Font.register(font);
|
||
|
else
|
||
|
Font.register(font);
|
||
|
return font;
|
||
|
}
|
||
|
|
||
|
});
|
||
|
|
||
|
var round = Math.round;
|
||
|
|
||
|
var FlippedVMLPathAtNormalPrecision = Class(Path, {
|
||
|
|
||
|
initialize: function(path){
|
||
|
this.reset();
|
||
|
if (path){
|
||
|
this.push(path);
|
||
|
}
|
||
|
},
|
||
|
|
||
|
onReset: function(){
|
||
|
this.path = [];
|
||
|
},
|
||
|
|
||
|
onMove: function(sx, sy, x, y){
|
||
|
this.path.push('m', round(x), -round(y));
|
||
|
},
|
||
|
|
||
|
onLine: function(sx, sy, x, y){
|
||
|
this.path.push('l', round(x), -round(y));
|
||
|
},
|
||
|
|
||
|
onBezierCurve: function(sx, sy, p1x, p1y, p2x, p2y, x, y){
|
||
|
this.path.push('c',
|
||
|
round(p1x), -round(p1y),
|
||
|
round(p2x), -round(p2y),
|
||
|
round(x), -round(y)
|
||
|
);
|
||
|
},
|
||
|
|
||
|
onClose: function(){
|
||
|
this.path.push('x');
|
||
|
},
|
||
|
|
||
|
toVML: function(){
|
||
|
return this.path.join(' ');
|
||
|
}
|
||
|
|
||
|
});
|