view nodejs/svg.js @ 761:cf60cf8983f8

Add modeline
author wycc
date Sat, 28 Aug 2010 23:09:58 +0800
parents ae1ae29348d1
children 77b561bb7929
line wrap: on
line source

var libxml = require('libxmljs');
var sys=require('sys');
var mbfly = require("mbfly");
var ldr = mbfly.img_ldr_new(".");


var _std_colors = {
    "white": [1, 1, 1],
    "black": [0, 0, 0],
    "red": [1, 0, 0]
};

exports.loadSVG=function(mb_rt,root,filename) {
    return new loadSVG(mb_rt, root, filename);
};

function loadSVG(mb_rt, root, filename) {
    var doc = libxml.parseXmlFile(filename);
    var nodes = doc.root().childNodes();
    var coord = mb_rt.coord_new(root);
	var k;
	var accu=[1,0,0,0,1,0];
	this.mb_rt = mb_rt;
	this.stop_ref={};
	this.gradients={};

    for(k in nodes) {
	    var n = nodes[k].name();
		if (n == "defs") {
		    this.parseDefs(root,nodes[k]);
		} else if (n == "g") {
		    this.parseGroup(accu,root,'root_coord',nodes[k]);
		} 
    }
}

function make_mbnames(mb_rt, n, obj) {
    var mbname;
    var name;
    
    if(!mb_rt.mbnames)
	mb_rt.mbnames = {};
    
    mbname = n.attr("mbname");
    if(mbname) {
	name = mbname.value();
	mb_rt.mbnames[name] = obj;
    }
}

function getInteger(n,name)
{
    if (n == null) return 0;
    var a = n.attr(name);
	if (a==null) return 0;
	return parseInt(a.value());
}

function parsePointSize(s)
{
    var fs=0;
	var i;

	for(i=0;i<s.length;i++) {
	    if (s[i]<'0' || s[i] > '9') break;
		fs = fs*10 + (s[i]-'0');
	}
	return fs;
	
}

function parseColor(c)
{
    if (c[0] == '#') {
	    return parseInt(c.substring(1,3),16)<<16 | parseInt(c.substring(3,5),16)<<8 | parseInt(c.substring(5,7),16);
	}
}

function parseTextStyle(style,n)
{
	var attr;
    if (n) {
        attr = n.attr('style');
	} else {
	    attr = null;
	}
	if (attr == null) {
		return;
	}
	var f = attr.value().split(';');

	for(i in f) {
	    var kv = f[i].split(':');
		if (kv[0] == 'font-size') {
		    style.fs = parsePointSize(kv[1]);
		} else if (kv[0] == "font-style") {
		} else if (kv[0] == "font-weight") {
		} else if (kv[0] == "fill") {
		    style.color = parseColor(kv[1]);
		} else if (kv[0] == "fill-opacity") {
		} else if (kv[0] == "stroke-opacity") {
		} else if (kv[0] == "stroke") {
		} else if (kv[0] == "stroke-width") {
		} else if (kv[0] == "stroke-linecap") {
		} else if (kv[0] == "stroke-linejoin") {
		} else if (kv[0] == "stroke-lineopacity") {
		} else if (kv[0] == "font-family") {
		    style.family = kv[1];
		} else if (kv[0] == "font-stretch") {
		} else if (kv[0] == "font-variant") {
		} else if (kv[0] == "text-anchor") {
		} else if (kv[0] == "text-align") {
		} else if (kv[0] == "writing-mode") {
		} else if (kv[0] == "line-height") {
		} else {
		    sys.puts("Unknown style: "+kv[0]);
		}
	}
}
function tspan_set_text(text)
{
   this.text.set_text(text); 
}

loadSVG.prototype.parseTSpan=function(coord, n,style)
{
    var x = getInteger(n,'x');
    var y = getInteger(n,'y');
	var tcoord = this.mb_rt.coord_new(coord);
	var nodes = n.childNodes();
	var k;

    var obj = this.mb_rt.stext_new(n.text(),x,y);
    parseTextStyle(style,n);
    style.paint = this.mb_rt.paint_color_new(1,1,1,1);
    style.face=this.mb_rt.font_face_query(style.family, 2, 100);
	obj.set_style([[20,style.face,style.fs]]);
	style.paint.fill(obj);
	tcoord.add_shape(obj);
	for(k in nodes) {
	    var name = nodes[k].name();
		if (name == "tspan") {
		    this.parseTSpan(tcoord,nodes[k]);
		} else {
		}
	}
	tcoord.set_text=tspan_set_text;
	tcoord.text = obj;
    make_mbnames(this.mb_rt, n, tcoord);
}

loadSVG.prototype._prepare_paint_color=function(color, alpha) {
    var paint;
    var c;
    
    if (color[0]=='#') {
	var r,g,b;
	r = parseInt(color.substring(1,3),16)/255;
	g = parseInt(color.substring(3,5),16)/255;
	b = parseInt(color.substring(5,7),16)/255;
	paint = this.mb_rt.paint_color_new(r, g, b, alpha);
    } else if(_std_colors[color]) {
	c = _std_colors[color];
	paint = this.mb_rt.paint_color_new(c[0], c[1], c[2], alpha);
    } else {
	paint = this.mb_rt.paint_color_new(0,0,0,1);
    }
    return paint;
}

function guessPathBoundingBox(coord,d)
{
    var items = d.split(' ');
	var len = items.length;
	var pair;
	var i;
	var minx,miny;

	minx = 10000;
	miny = 10000;

	for(i=0;i<len;i++) {
	    var type = items[i].toLowerCase();
		x = minx;y = miny;
		switch(type) {
		    case 'm':
			case 'l':
			case 'a':
			case 'x':
			    pair = items[i+1].split(',');
				if (pair.length==2) {
				    x = parseFloat(pair[0]);
					y = parseFloat(pair[1]);
					i++;
				} else {
				    x = parseFloat(items[i+1]);
				    y = parseFloat(items[i+2]);
					i+=2;
				}
				break;
			case 'q':
				// Implement this latter
				break;
			case 'c':
				// Implement this latter
				break;
			case 's':
				// Implement this latter
				break;
			case 'h':
				x = parseFloat(items[i+1]);
				break;
			case 'v':
				y = parseFloat(items[i+1]);
				break;
		}
		if (x < minx) minx = x;
		if (y < miny) miny = y;
	}
	if (coord.center.x >  minx)
		coord.center.x = minx;
	if (coord.center.y >  miny)
		coord.center.y = miny;
}


loadSVG.prototype.parsePath=function(coord,id, n)
{
    var d = n.attr('d').value();
    var style = n.attr('style');
    var path = this.mb_rt.path_new(d);
    var paint;
    var fill_alpha = 1;
    var stroke_alpha = 1;
    var fill_color;
    var stroke_color;
    var black_paint;
    
    guessPathBoundingBox(coord,d);
    if(style != null) {
	var items = style.value().split(';');
	var alpha;
	
	for(i in items) {
	    var f = items[i].split(':');
	    if (f[0] == 'opacity') {
		alpha = f[1];
	    } else if (f[0] == 'fill') {
		fill_color = f[1];
	    } else if (f[0] == 'fill-opacity') {
		fill_alpha = parseFloat(f[1]);
	    } else if (f[0] == 'stroke') {
		stroke_color = f[1];
	    } else if (f[0] == 'stroke-width') {
		path.stroke_width = parseFloat(f[1]);
	    } else if (f[0] == 'stroke-opacity') {
		stroke_alpha = parseFloat(f[1]);
	    }
	}

    }

    if(!fill_color || !stroke_color)
	black_paint = this.mb_rt.paint_color_new(0, 0, 0, 1);
    
    if(fill_color) {
	if(fill_color != "none") {
	    paint = this._prepare_paint_color(fill_color, fill_alpha);
	    paint.fill(path);
	    sys.puts("paint path with "+fill_color+" "+fill_alpha);
	}
    } else {
	black_paint.fill(path);
    }
    if(stroke_color) {
	if(stroke_color != "none") {
	    paint = this._prepare_paint_color(stroke_color, stroke_alpha);
	    paint.stroke(path);
	    sys.puts("stroke path");
	}
    } else {
	black_paint.stroke(path);
    }
    coord.add_shape(path);

    make_mbnames(this.mb_rt, n, path);
}

loadSVG.prototype.parseText=function(coord,id, n)
{
    var x = getInteger(n,'x');
    var y = getInteger(n,'y');
	var tcoord = this.mb_rt.coord_new(coord);
    var style = new Object();

    if (n.attr('x'))
  	    if (coord.center.x > x)
		    coord.center.x = x;
	if (n.attr('y'))
	    if (coord.center.y > y)
		    coord.center.y = y;
	style.fs = 20;
	style.family = 'courier';
	parseTextStyle(style,n);
	var nodes = n.childNodes();
	var k;
	for(k in nodes) {
	    var c= nodes[k].name();
		if (c == "tspan") {
	        this.parseTSpan(tcoord,nodes[k],style);
		} else {
		}
	}
	
    make_mbnames(this.mb_rt, n, tcoord);
}


function multiply(s,d) {
    var m=[];
	m[0] = s[0]*d[0]+s[1]*d[3];
	m[1] = s[0]*d[1]+s[1]*d[4];
	m[2] = s[0]*d[2]+s[1]*d[5]+s[2];
	m[3] = s[3]*d[0]+s[4]*d[3];
	m[4] = s[3]*d[1]+s[4]*d[4];
	m[5] = s[3]*d[2]+s[4]*d[5]+s[5];
	s[0] = m[0];
	s[1] = m[1];
	s[2] = m[2];
	s[3] = m[3];
	s[4] = m[4];
	s[5] = m[5];
}

function parseTransform(coord, s)
{
    var off = s.indexOf('translate');
	if (off != -1) {
	    var ss = s.substring(off+9);
		for(i=0;i<ss.length;i++) {
		    if (ss[i] == '(') break;
		}
		ss = ss.substring(i+1);
		for(i=0;i<ss.length;i++) {
		    if (ss[i] == ')') {
				ss = ss.substring(0,i);
				break;
			}
		}
		var f = ss.split(',');
		var x,y;
		x = parseFloat(f[0]);
		y = parseFloat(f[1]);
		coord[2] += x;
		coord[5] += y;
	}
	off = s.indexOf('matrix');
	if (off != -1) {
		var end = s.indexOf(')');
		var m = s.substring(7,end);
		var fields = m.split(',');
		var newm=[];
		newm[0] = parseFloat(fields[0]);
		newm[1] = parseFloat(fields[2]);
		newm[2] = parseFloat(fields[4]);
		newm[3] = parseFloat(fields[1]);
		newm[4] = parseFloat(fields[3]);
		newm[5] = parseFloat(fields[5]);
		multiply(coord,newm);
	}
}

loadSVG.prototype.parseRect=function(accu_matrix,coord, id, n) 
{
    sys.puts("rect");
    var x = getInteger(n,'x');
    var y = getInteger(n,'y');
	var rx,ry;
    var w = getInteger(n,'width');
    var h = getInteger(n,'height');
	var trans = n.attr('transform');
	var paint;
	
	var style = n.attr('style');

	if (trans) {
		parseTransform(coord,trans.value());
		var m = [1,0,0,0,1,0];
		parseTransform(m,trans.value());
		rx = x+m[2];
		ry = y+m[5];
	}

	if (coord.center.x > rx)
		coord.center.x = rx;
	if (coord.center.y > ry)
		coord.center.y = ry;
	coord.center.x += accu_matrix[2];
	coord.center.y += accu_matrix[5];

	if (style==null) {
		paint = this.mb_rt.paint_color_new(0,0,0,0.1);
	} else {
	    var items = style.value().split(';');
		var fill = '';
		var alpha = 1;
		for(i in items) {
			var f = items[i].split(':');
			if (f[0] == 'opacity') {
			    alpha = f[1];
			} else if (f[0] == 'fill') {
			    fill = f[1];
			} else if (f[0] == 'fill-opacity') {
			} else if (f[0] == 'stroke') {
			} else if (f[0] == 'stroken-width') {
			} else if (f[0] == 'stroke-opacity') {
			}
		}
		if (fill[0]=='#') {
		    var r,g,b;
			r = parseInt(fill.substring(1,3),16)/256;
			g = parseInt(fill.substring(3,5),16)/256;
			b = parseInt(fill.substring(5,7),16)/256;

		    paint = this.mb_rt.paint_color_new(r,g,b,parseFloat(alpha));
		} else if (fill.substring(0,3) == 'url') {
		    var id = fill.substring(5,fill.length-1);
			var gr = this.gradients[id];
			paint = this.mb_rt.paint_linear_new(gr[0],gr[1],gr[2],gr[3]);
			paint.set_stops(this.stop_ref[id]);
		} else {
	        paint = this.mb_rt.paint_color_new(0,0,0,1);
		}
	}
	var rect = this.mb_rt.rect_new(x,y,w,h,10, 10);
	paint.fill(rect);
	coord.add_shape(rect);
}

loadSVG.prototype.parseGroup=function(accu_matrix,root, group_id, n)
{
    var k;
    var nodes = n.childNodes();
    var coord = this.mb_rt.coord_new(root);
	// Parse the transform and style here
	var trans = n.attr('transform');
	var accu=[1,0,0,0,1,0];
    coord.center= new Object();
	coord.center.x = 10000;
	coord.center.y = 10000;
	if (trans!=null) {
	    parseTransform(coord, trans.value());
	} 
	accu[2] = accu_matrix[2]+coord[2];
	accu[5] = accu_matrix[5]+coord[5];


	for(k in nodes) {
	    var c = nodes[k].name();
		var attr = nodes[k].attr('id');
		var id;
		if (attr) {
		    id = attr.value();
		}
		if (c == "g") {
		    this.parseGroup(accu,coord, id, nodes[k]);
		} else if (c == "path") {
		    this.parsePath(coord, id, nodes[k]);
		} else if (c == "text") {
		    this.parseText(coord, id, nodes[k]);
		} else if (c == "rect") {
		    this.parseRect(accu_matrix,coord, id, nodes[k]);
		} else if (c == "image") {
			this.parseImage(coord, id, nodes[k]);
		}
	}
    make_mbnames(this.mb_rt, n, coord);
}

loadSVG.prototype.parseImage=function(coord,id, n)
{
	var ref = n.attr('href').value();

	if (ref == null) return;
	if (ref.substr(0,7) == "file://") {
	    ref = ref.substring(7);
	} else if (ref.substr(0,5)=="file:") {
	    ref = ref.substring(5);
	} else {
	    return;
	}
	var w;
	var h;
	var x,y;

	w = n.attr("width");
	if (w == null) return;
	w = parseFloat(w.value());
	h = n.attr("height");
	if (h == null) return;
	h = parseFloat(h.value());	
	x = n.attr("x");
	if (x == null) return;
	x = parseFloat(x.value());
	y = n.attr("y");
	if (y == null) return;
	y = parseFloat(y.value());
	if (coord.center.x > x)
		coord.center.x = x;
	if (coord.center.y > y)
		coord.center.y = y;
	var img = this.mb_rt.image_new(x,y,w,h);
	var img_data = ldr.load(ref);
	var paint = this.mb_rt.paint_image_new(img_data);
	paint.fill(img);
	coord.add_shape(img);
    make_mbnames(this.mb_rt, n, img);
}

loadSVG.prototype._MB_parseLinearGradient=function(root,n)
{
    var id = n.attr('id');
	var k;
	var nodes = n.childNodes();

	if (id == null) return;
	var x1 = n.attr("x1");
	var y1 = n.attr("y1");
	var x2 = n.attr("x2");
	var y2 = n.attr("y2");
	var gr;
	var color, opacity;
	var stops;
	var r,g,b;
    stops=[];
	for(k in nodes) {
	    var ss = nodes[k];
	    if (ss.name()=="stop") {
		     var style = ss.attr("style").value();
			 var items = style.split(';');
			 var off = parseInt(ss.attr('offset').value());
			 color = 'black';
			 opacity = 1;
			 for (i in items) {
			     it = items[i];
			     var f = it.split(':');
				 k = f[0];
				 v = f[1];
				 if (k == 'stop-color') {
				     color = v.substring(1);
					 if (v == 'white') {
					     r = 1;
						 g = 1;
						 b = 1;
					 } else if (v == 'black') {
					     r = 0;
						 g = 0;
						 b = 0;
					 } else {
					     r = parseInt(color.substring(0,2),16)/255.0;
					     g = parseInt(color.substring(2,4),16)/255.0;
					     b = parseInt(color.substring(4,6),16)/255.0;
					}
				 } else if (k=='stop-opacity') {
				     opacity = parseFloat(v);
				 }
			 }
			 stops.push([off, r,g,b,opacity]);
		}
	}
	var href = n.attr('href');
	if (href != null) {
	    href = href.value();
	    pstops = this.stop_ref[href.substring(1)];
		stops = pstops.concat(stops);
	}
	id = id.value();
	this.stop_ref[id] = stops;
	if (x1)
	    x1 = parseFloat(x1.value());
	if (x2)
	    x2 = parseFloat(x2.value());
	if (y1)
	    y1 = parseFloat(y1.value());
	if (y2)
	    y2 = parseFloat(y2.value());
	this.gradients[id] = [x1,y1,x2,y2];
}

loadSVG.prototype.parseDefs=function(root,n) 
{
    var k;
	var nodes = n.childNodes();
    
	for(k in nodes) {
	    var name = nodes[k].name();
	    if (name == "linearGradient") {
		    this._MB_parseLinearGradient(root,nodes[k]);
		}
	}
}