GT2/GT2-iOS/node_modules/art/parsers/svg/fonts.js

204 lines
5.2 KiB
JavaScript
Raw Normal View History

2018-02-12 17:26:06 +00:00
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(' ');
}
});