/***************************************************************************
 *   Copyright (C) 2004 by Juergen Thies                                   *
 *   layout@juergenthies.de                                                *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/
#define _USE_MATH_DEFINES
#include "svg.h"
#include <qfile.h>
#include <qstring.h>
#include <qtextstream.h>
#include "general/drawingfield.h"
#include "elements/pointarray.h"
#include "general/layers.h"
#include "elements/celllist.h"
#include "elements/cell.h"
#include "elements/path.h"
#include "elements/polygon.h"
#include "general/setup.h"
#include "elements/element.h"
#include <qstringlist.h>
#include "layout.h"
#include "elements/pointarray.h"

//drawingField *svg::df=NULL;
//QTextStream *svg::str=NULL;

svg::svg(QObject *parent, const char *)
 : QObject(parent)
{
}


svg::~svg()
{
}


void svg::open(QString fileName,drawingField *d){
  svg svgClass;
  svgClass.load(fileName,d,fileOpen);
}

void svg::import(QString fileName,drawingField *d){
  svg svgClass;
  svgClass.load(fileName,d,fileImport);
}

void svg::update(QString fileName,drawingField *d){
  svg svgClass;
  svgClass.load(fileName,d,fileUpdate);
}

void svg::load(QString fileName,drawingField *d,fileOpenType typ){
  if (typ==fileImport) {
  	report.setTitle(tr("Import of SVG-File \"")+fileName+"\"");}
  else if (typ==fileOpen){
	report.setTitle(tr("Open of SVG-File \"")+fileName+"\"");}
  else if (typ==fileUpdate){
	report.setTitle(tr("Update with SVG-File \"")+fileName+"\"");}
try { 
  QFile f( fileName );
  if ( !f.open( QIODevice::ReadOnly ) )
	throw QString(tr("Can not open File."));
  cellList *firstcellhelp=d->firstCell;
  d->firstCell=NULL;
  df=d;
  mainCell=d->addCell()->thisCell;
  d->currentCell=mainCell;
  svgTimer.start();
  read(&f);
  d->stripUnneeded();
  if (typ==fileImport) filegeneral::import(&report,d,firstcellhelp);
  if (typ==fileUpdate) filegeneral::update(&report,d,firstcellhelp);

  }
catch (QString s){
  report.addItem(tr("Aborted. "),0);
  report.addItem(s,1);
 }
  //report.showReport(); 
  QString s=report.getReport();
  d->showReport(s,report.getLastRang());
//printf("3\n");
}

void svg::read(QIODevice *device)
{
    xml=new QXmlStreamReader();
    xml->setDevice(device);
    cl=new cellList();
    cl->thisCell=mainCell;
    c=mainCell;
    cl->nextCell=NULL;
    while (!xml->atEnd()) {
        xml->readNext();
	//printf("main: %s\n",name().toString().toAscii().data());
        if (xml->isStartElement()) {
            if (xml->name() == "svg" )
              readSvg();
            else 
		{
               report.addItem(tr("This is not a svg file/Unknow main entry"),2,xml->name().toString());
		readEnd();
		}
        }
    }
    delete xml;
    xml=NULL;
df->showMessage(tr("%1 records loaded.").arg(counter));
}

void svg::readSvg(){
//printf("start svg\n");
 bool ownId=false; 
    while (!xml->atEnd()) {
        xml->readNext();
        //printf("xml->name %s\n",xml->name().toString().toAscii().data());
        counter++;
	if (svgTimer.elapsed()>200){
		df->showMessage(tr("%1 records loaded.").arg(counter));
		svgTimer.start();
		}
        if (xml->isEndElement())
            break;

        if (xml->isStartElement()) {
	    if (xml->attributes().value("id").toString()!=""){
			cell *nc=df->addCell()->thisCell;
			nc->cellName=xml->attributes().value("id").toString();
			//printf("start cell %s\n",nc->cellName.toAscii().data());
			c=nc;
			cellList *clh=cl;
			cl=new cellList;
			cl->nextCell=clh;
			cl->thisCell=c;
			ownId=true;
		}
	//printf("svg: %s \n",xml->name().toString().toAscii().data());
		element *e=getElement();
	    if (e!=NULL){
			for (cellList *cl2=cl;cl2!=NULL;cl2=cl2->nextCell){
				if ((cl2->thisCell!=c)&&(cl2->thisCell!=NULL)){
					cl2->thisCell->addElement(e->copy());
				}
				}
			
		}
	    else if (c==NULL){
		readEnd();
		}
  	    else if (xml->name() == "title"){
		QString t=xml->readElementText ();
		c->cellName=t;
		//report.addItem(tr("title"),3,xml->attributes().value("cx").toString());
		//readEnd();
		}
	    else if (xml->name() == "desc"){
		report.addItem(tr("description"),5,xml->attributes().value("cx").toString());
		readEnd();
		}
            else if ((xml->name() == "g")||(xml->name() == "svg")||(xml->name() == "symbol")||(xml->name() == "defs")){
		bool defs=false;
		if (xml->name() == "defs")defs=true;
		QString t=xml->attributes().value("transform").toString();
		if (defs) for (cellList *cl2=cl;cl2!=NULL;cl2=cl2->nextCell){
			if (mainCell==cl2->thisCell)cl2->thisCell=NULL;
		}
		elementList *el=c->firstElement;
		readSvg();
		elementList *el2=c->firstElement;
		int numElements=0;
		while (el2!=el){
			if (el2->thisElement!=NULL)
			processTransform(el2->thisElement,t);
			numElements++;
			el2=el2->nextElement;
			if (el2==NULL)el2=el;}
		for (cellList *cl2=cl;cl2!=NULL;cl2=cl2->nextCell){
				if ((cl2->thisCell!=c)&&(cl2->thisCell!=NULL)){
					el2=cl2->thisCell->firstElement;
					for (int i=0;i<numElements;i++){
						if (el2->thisElement!=NULL)
						processTransform(el2->thisElement,t);
						el2=el2->nextElement;
					}
				}
				}
		if (defs) for (cellList *cl2=cl;cl2!=NULL;cl2=cl2->nextCell){
			if (NULL==cl2->thisCell)cl2->thisCell=mainCell;
		}
		}
	    else if (xml->name() == "metadata"){readEnd(true);}
	    else if (xml->name() == "namedview"){readEnd(true);}
            else {
                report.addItem(tr("unknow/unsupported element"),2,xml->name().toString());
		readEnd();
		}
	    if (ownId){
		cellList *clh=cl;
		cl=cl->nextCell;
		//printf("cell %s\n",clh->thisCell->cellName.toAscii().data());
		clh->thisCell=NULL;
		clh->nextCell=NULL;
		delete clh;
		c=cl->thisCell;
		if (c==NULL)c=mainCell;
		ownId=false;
		//printf("end cell\n");
		}

        }
    }
//printf("end svg\n");
}


element * svg::getElement(){
   element *e=NULL;
//printf("name: %s\n",xml->name().toString().toAscii().data());
   if (xml->name() == "rect") {
	int x=0;
	int y=0;
	if (xml->attributes().value("x")!="")x=getInt(xml->attributes().value("x"));
	if (xml->attributes().value("y")!="")y=-getInt(xml->attributes().value("y"));
	int rx=0;
	int ry=0;
	if (xml->attributes().value("rx")!="")rx=getInt(xml->attributes().value("rx"));
	if (xml->attributes().value("ry")!="")ry=getInt(xml->attributes().value("ry"));
	if (ry>rx) rx=ry;
	e=NULL;
	if  (rx>0) e=c->addRoundedBox(x,y,getInt(xml->attributes().value("width")),-getInt(xml->attributes().value("height")),rx,getLayer());
	else
	e=c->addBox(x,y,getInt(xml->attributes().value("width")),-getInt(xml->attributes().value("height")),getLayer());
	processTransform(e,xml->attributes().value("transform").toString());
	//report.addItem(tr("rect"),3,attributes().value("width").toString());
	readEnd();
	}
   else if (xml->name() == "circle"){
	int x=0;
	int y=0;
	int r=0;
	if (xml->attributes().value("cx")!="")x=getInt(xml->attributes().value("cx"));
	if (xml->attributes().value("cy")!="")y=-getInt(xml->attributes().value("cy"));
	if (xml->attributes().value("r")!="")r=getInt(xml->attributes().value("r"));
	e=c->addCircle(getLayer(),QPoint(x,y),r);
	processTransform(e,xml->attributes().value("transform").toString());
	//report.addItem(tr("circle"),3,attributes().value("cx").toString());
	readEnd();
	}
   else if (xml->name() == "ellipse"){
	int x=0;
	int y=0;
	int rx=0;
	int ry=0;
	if (xml->attributes().value("cx")!="")x=getInt(xml->attributes().value("cx"));
	if (xml->attributes().value("cy")!="")y=-getInt(xml->attributes().value("cy"));
	if (xml->attributes().value("rx")!="")rx=getInt(xml->attributes().value("rx"));
	if (xml->attributes().value("ry")!="")ry=getInt(xml->attributes().value("ry"));
	e=c->addEllipse(getLayer(),QPoint(x,y),rx,ry);
	processTransform(e,xml->attributes().value("transform").toString());
	//report.addItem(tr("ellipse"),3,attributes().value("x1").toString());
	readEnd();
	}
   else if (xml->name() == "line"){
	int x1=0;
	int y1=0;
	int x2=0;
	int y2=0;
	if (xml->attributes().value("x1")!="")x1=getInt(xml->attributes().value("x1"));
	if (xml->attributes().value("y1")!="")y1=-getInt(xml->attributes().value("y1"));
	if (xml->attributes().value("x2")!="")x2=getInt(xml->attributes().value("x2"));
	if (xml->attributes().value("y2")!="")y2=-getInt(xml->attributes().value("y2"));
	pointArray pa;
	pa<<QPoint(x1,y1);
	pa<<QPoint(x2,y2);
	e=c->addPath(pa,getLayer());
	setWidth(e);
	setCap(e);
// 	processTransform(e,xml->attributes().value("transform").toString());
	//report./*addI*/tem(tr("line"),3,attributes().value("x1").toString());
	readEnd();
	}
   else if (xml->name() == "polyline"){
	pointArray pa;
	QString points=xml->attributes().value("points").toString();
	pa=getPoints(points);
	e=c->addPath(pa,getLayer());
	setWidth(e);
	processTransform(e,xml->attributes().value("transform").toString());
	//report.addItem(tr("polyline"),3,xml->attributes().value("x1").toString());
	readEnd();
	}
   else if (xml->name() == "polygon"){
	pointArray pa;
	QString points=xml->attributes().value("points").toString();
	pa=getPoints(points);
	e=c->addPolygon(pa,getLayer());
	processTransform(e,xml->attributes().value("transform").toString());
	//report.addItem(tr("polyline"),3,xml->attributes().value("x1").toString());
	readEnd();
	}
   else if (xml->name() == "path"){
	QString shelp=xml->attributes().value("d").toString();
	shelp=shelp.replace("m"," m ");
	shelp=shelp.replace("M"," M ");
	shelp=shelp.replace("l"," l ");
	shelp=shelp.replace("L"," L ");
	shelp=shelp.replace("V"," V ");
	shelp=shelp.replace("v"," v ");
	shelp=shelp.replace("h"," h ");
	shelp=shelp.replace("H"," H ");
	shelp=shelp.replace("c"," c ");
	shelp=shelp.replace("C"," C ");
	shelp=shelp.replace("z"," z ");
	shelp=shelp.replace("Z"," Z ");
	shelp=shelp.replace("s"," s ");
	shelp=shelp.replace("S"," S ");
	shelp=shelp.replace("q"," q ");
	shelp=shelp.replace("Q"," Q ");
	shelp=shelp.replace("t"," t ");
	shelp=shelp.replace("T"," T ");
	shelp=shelp.replace("a"," a ");
	shelp=shelp.replace("A"," A ");
	shelp=shelp.replace("-"," -");
	//printf("%s\n",shelp.toAscii().data());
	QStringList sl=shelp.split(QRegExp("[  ,\n\r\t]{1,1000}"),QString::SkipEmptyParts);
	pointArray pa;
	//printf("start path}\n");
	QPoint last;
	QPoint bezierhelp=QPoint(0,0);
	for (int i=0;i<sl.size();i++){
		//printf("path -> %s\n",sl[i].toAscii().data());
		if (sl[i]=="M") {
			last=QPoint( element::runden(sl[i+1].toDouble()/df->userunits), -element::runden(sl[i+2].toDouble()/df->userunits));
		i+=2;
		pa<<last;
		}
		else if (sl[i]=="m") {
			last+=QPoint( element::runden(sl[i+1].toDouble()/df->userunits), -element::runden(sl[i+2].toDouble()/df->userunits));
		i+=2;
		pa<<last;
		}
		else if (sl[i]=="L") {
			last=QPoint( element::runden(sl[i+1].toDouble()/df->userunits), -element::runden(sl[i+2].toDouble()/df->userunits));
		i+=2;
		pa<<last;
		}
		else if (sl[i]=="l") {
			last+=QPoint( element::runden(sl[i+1].toDouble()/df->userunits), -element::runden(sl[i+2].toDouble()/df->userunits));
		i+=2;
		pa<<last;
		}
		else if (sl[i]=="V") {
			last=QPoint( last.x(), -element::runden(sl[i+1].toDouble()/df->userunits));
		i+=1;
		pa<<last;
		}
		else if (sl[i]=="v") {
			last+=QPoint( 0, -element::runden(sl[i+1].toDouble()/df->userunits));
		i+=1;
		pa<<last;
		}
		else if (sl[i]=="H") {
			last=QPoint(element::runden(sl[i+1].toDouble()/df->userunits), last.y());
		i+=1;
		pa<<last;
		}
		else if (sl[i]=="h") {
			last+=QPoint(element::runden(sl[i+1].toDouble()/df->userunits), 0 );
		i+=1;
		pa<<last;
		}
		else if ((sl[i]=="z")||(sl[i]=="Z")) {
			last=pa.point(0);
		pa<<last;
		}
		else if (sl[i]=="C") {
			QPoint p1=QPoint( element::runden(sl[i+1].toDouble()/df->userunits), -element::runden(sl[i+2].toDouble()/df->userunits));
			QPoint p2=QPoint( element::runden(sl[i+3].toDouble()/df->userunits), -element::runden(sl[i+4].toDouble()/df->userunits));
			QPoint p3=QPoint( element::runden(sl[i+5].toDouble()/df->userunits), -element::runden(sl[i+6].toDouble()/df->userunits));
		i+=6;
		pa<<element::bezier3(last,p3,p1,p2,setup::defaultBezierIteration);
		last=p3;
		bezierhelp=2*p3-p2;
		}
		else if (sl[i]=="c") {
			QPoint p1=last+QPoint( element::runden(sl[i+1].toDouble()/df->userunits), -element::runden(sl[i+2].toDouble()/df->userunits));
			QPoint p2=last+QPoint( element::runden(sl[i+3].toDouble()/df->userunits), -element::runden(sl[i+4].toDouble()/df->userunits));
			QPoint p3=last+QPoint( element::runden(sl[i+5].toDouble()/df->userunits), -element::runden(sl[i+6].toDouble()/df->userunits));
		i+=6;
		pa<<element::bezier3(last,p3,p1,p2,setup::defaultBezierIteration);
		last=p3;
		bezierhelp=2*p3-p2;
		}
		else if (sl[i]=="S") {
			QPoint p1=QPoint( element::runden(sl[i+1].toDouble()/df->userunits), -element::runden(sl[i+2].toDouble()/df->userunits));
			QPoint p2=QPoint( element::runden(sl[i+3].toDouble()/df->userunits), -element::runden(sl[i+4].toDouble()/df->userunits));
		i+=4;
		pa<<element::bezier3(last,p2,bezierhelp,p1,setup::defaultBezierIteration);
		last=p2;
		bezierhelp=2*p2-p1;
		}
		else if (sl[i]=="s") {
			QPoint p1=last+QPoint( element::runden(sl[i+1].toDouble()/df->userunits), -element::runden(sl[i+2].toDouble()/df->userunits));
			QPoint p2=last+QPoint( element::runden(sl[i+3].toDouble()/df->userunits), -element::runden(sl[i+4].toDouble()/df->userunits));
		i+=4;
		pa<<element::bezier3(last,p2,bezierhelp,p1,setup::defaultBezierIteration);
		last=p2;
		bezierhelp=2*p2-p1;
		}
		else if (sl[i]=="Q") {
			QPoint p1=QPoint( element::runden(sl[i+1].toDouble()/df->userunits), -element::runden(sl[i+2].toDouble()/df->userunits));
			QPoint p2=QPoint( element::runden(sl[i+3].toDouble()/df->userunits), -element::runden(sl[i+4].toDouble()/df->userunits));
		i+=4;
		pa<<element::bezier2(last,p2,p1,setup::defaultBezierIteration);
		last=p2;
		bezierhelp=2*p2-p1;
		}
		else if (sl[i]=="q") {
			QPoint p1=last+QPoint( element::runden(sl[i+1].toDouble()/df->userunits), -element::runden(sl[i+2].toDouble()/df->userunits));
			QPoint p2=last+QPoint( element::runden(sl[i+3].toDouble()/df->userunits), -element::runden(sl[i+4].toDouble()/df->userunits));
		i+=4;
		pa<<element::bezier2(last,p2,p1,setup::defaultBezierIteration);
		last=p2;
		bezierhelp=2*p2-p1;
		}
		else if (sl[i]=="T") {
			QPoint p1=QPoint( element::runden(sl[i+1].toDouble()/df->userunits), -element::runden(sl[i+2].toDouble()/df->userunits));
		i+=2;
		pa<<element::bezier2(last,p1,bezierhelp,setup::defaultBezierIteration);
		last=p1;
		bezierhelp=2*p1-bezierhelp;
		}
		else if (sl[i]=="t") {
			QPoint p1=last+QPoint( element::runden(sl[i+1].toDouble()/df->userunits), -element::runden(sl[i+2].toDouble()/df->userunits));
		i+=2;
		pa<<element::bezier2(last,p1,bezierhelp,setup::defaultBezierIteration);
		last=p1;
		bezierhelp=2*p1-bezierhelp;
		}
		else if ((sl[i]=="A")||(sl[i]=="a")) {
			//printf("%s %s %s %s %s %s %s %s %d %d\n",sl[i].toAscii().data(),sl[i+1].toAscii().data(),sl[i+2].toAscii().data(),sl[i+3].toAscii().data(),sl[i+4].toAscii().data(),sl[i+5].toAscii().data(),sl[i+6].toAscii().data(),sl[i+7].toAscii().data(),last.x(),last.y());
			double rx =element::runden(sl[i+1].toDouble()/df->userunits);
			double ry =element::runden(sl[i+2].toDouble()/df->userunits);
			double fi =sl[i+3].toDouble();
			int fa=sl[i+4].toInt();
			int fs=sl[i+5].toInt();
			QPoint p1;
			if (sl[i]=="A") p1=QPoint( element::runden(sl[i+6].toDouble()/df->userunits), -element::runden(sl[i+7].toDouble()/df->userunits));
			else p1=last+QPoint( element::runden(sl[i+6].toDouble()/df->userunits), -element::runden(sl[i+7].toDouble()/df->userunits));
		//printf("p1: %d %d %f %f\n",p1.x(),p1.y(),sl[i+6].toDouble()/df->userunits,sl[i+7].toDouble()/df->userunits);
		double xh=(last.x()-p1.x())/2;
		double yh=(last.y()-p1.y())/2;
		fi=fi/180*M_PI;
		double x_=xh*cos(fi)+yh*sin(fi);
		double y_=-xh*sin(fi)+yh*cos(fi);
		double delta=x_*x_/rx/rx+y_*y_/ry/ry;
		if (delta>1) {
			rx=sqrt(delta)*rx;
			ry=sqrt(delta)*ry;
			}
		xh=rx*y_/ry;
		yh=-ry*x_/rx;
		double sq=sqrt((rx*rx*ry*ry-rx*rx*y_*y_-ry*ry*x_*x_)/(rx*rx*y_*y_+ry*ry*x_*x_));
		if (fa!=fs) sq=-sq;
		double cx_=sq*xh;
		double cy_=sq*yh;
		xh=(last.x()+p1.x())/2;
		yh=(last.y()+p1.y())/2;
		double cx=cos(fi)*cx_-sin(fi)*cy_+xh;
		double cy=sin(fi)*cx_+cos(fi)*cy_+yh;
		double startAngle=element::angle(QPoint(1,0),QPoint(element::runden((x_+cx_)/rx),element::runden((y_-cy_)/ry)));
		startAngle=element::angle(QPoint(element::runden(cx),element::runden(cy)),QPoint(last.x(),last.y()));
		//if (startAngle>0) if (fs==0) startAngle-=360;
		//if (startAngle<0) if (fs==1) startAngle+=360;
		double endAngle=element::angle(QPoint(1,0),QPoint(element::runden((-x_+cx_)/rx),element::runden((-y_-cy_)/ry)));
		endAngle=element::angle(QPoint(element::runden(cx),element::runden(cy)),QPoint(p1.x(),p1.y()));
		bool swap=false;
		if (fs==1) {
			double help=startAngle;
			startAngle=endAngle;
			endAngle=help;
			swap=true;
		}
		if (endAngle<startAngle) {
			endAngle+=360;
		}
		/*printf("start %d %d\n",last.x(),last.y());
		printf("end %d %d\n",p1.x(),p1.y());
		printf("xy_ %d %d\n",(int)x_,(int)y_);
		printf("c_ %d %d\n",(int)cx_,(int)cy_);
		printf("center %d %d\n",(int)cx,(int)cy);
		printf("fi %f\n",fi);
		printf("startAngle %f\n",startAngle);
		printf("endAngle %f\n",endAngle);*/
		pointArray pa2;
		for (double ang=startAngle;ang<endAngle;ang+=setup::circularDefault){
			QPoint p=QPoint(element::runden(cos(fi)*rx*cos(ang/180*M_PI)-sin(fi)*ry*sin(ang/180*M_PI)+cx) , element::runden(sin(fi)*rx*cos(ang/180*M_PI)+cos(fi)*ry*sin(ang/180*M_PI)+cy));
			pa2<<p;
		}
		if (swap) pa2.flip();
		i+=7;
		pa<<pa2;
		pa<<p1;
		last=p1;
		}
	//printf("%d %d\n",last.x(),last.y());
	}
	if (pa.point(0)!=pa.point(pa.size()-1)) e=c->addPath(pa,getLayer());
	else e=c->addPolygon(pa,getLayer());
	setWidth(e);
	processTransform(e,xml->attributes().value("transform").toString());
	//report.addItem(tr("path"),3,attributes().value("cx").toString());
	readEnd();
	}
   else if (xml->name() == "text"){
	int x=0;
	int y=0;
	int width=0;
	QString id=xml->attributes().value("id").toString();
	if (xml->attributes().value("x")!="")x=getInt(xml->attributes().value("x"));
	if (xml->attributes().value("y")!="")y=-getInt(xml->attributes().value("y"));
	if (xml->attributes().value("font-size")!="")width=getInt(xml->attributes().value("font-size"));
	QString transform2=xml->attributes().value("transform").toString();
	int lay=getLayer();
	QString t=xml->text ().toString();
	xml->readNext();
	if (xml->isCharacters ())t+=xml->text ().toString();
	while (!xml->isEndElement()&&!xml->atEnd()){
			//printf("name %s, %s\n",xml->name().toString().toAscii().data(),t.toAscii().data());
			if (xml->isStartElement()) {//printf("new start\n");
					readEnd();xml->readNext();}
			else {xml->readNext();//printf("nostart\n");
				}
			if (xml->isCharacters ()) t+=xml->readElementText ();
	}
		
	e=c->addText(lay,QPoint(x,y),t);
	if (width!=0) e->setWidth(width);
	processTransform(e,transform2);
	//report.addItem(tr("text"),3,attributes().value("cx").toString());
	//readEnd();
	}
   else if (xml->name() == "use"){
	int x=0;
	int y=0;
	if (xml->attributes().value("x")!="")x=getInt(xml->attributes().value("x"));
	if (xml->attributes().value("y")!="")y=-getInt(xml->attributes().value("y"));
	QString ref=xml->attributes().value("xlink:href").toString();
	ref.remove("#");
	cell *refCell=df->findCell(ref);
	if (refCell!=NULL){
		e=c->addCellref(refCell,QPoint(x,y));
		processTransform(e,xml->attributes().value("transform").toString());
	}
	else {
		report.addItem(tr("refered cell not found"),2,ref);
		}
	//report.addItem(tr("use"),5,attributes().value("cx").toString());
	readEnd();
	}
  else return NULL;
return e;
}


void svg::processTransform(element *e,QString trans){
	QStringList sl=trans.split(QRegExp("[)\n\r\t]{1,1000}"),QString::SkipEmptyParts);
	for (int i=sl.size()-1;i>=0;i--){
		sl[i]=sl[i].trimmed();
		//printf("transform %s ->%s\n",sl[i].toAscii().data(),sl[i].section("(",1).toAscii().data());
		QStringList para=sl[i].section("(",1).split(QRegExp("[,()a-d f-z\n\r\t]{1,1000}"),QString::SkipEmptyParts);
		//for (int k=0;k<para.size();k++)printf("para %d: %s\n",k,para[k].toAscii().data());
		if (sl[i].trimmed()==""){
			}	
		else if (sl[i].left(9)=="translate"){
			int x=0;
			int y=0;
			if (para.size()>0) x=element::runden(para[0].toDouble()/df->userunits);
			if (para.size()>1) y=element::runden(-para[1].toDouble()/df->userunits);
			e->move(QPoint(x,y));
		}
		else if (sl[i].left(5)=="scale"){
			double sx=1;
			double sy=1;
			if (para.size()>0) sx=(para[0].toDouble());
			sy=sx;
			if (para.size()>1) sy=(para[1].toDouble());
			strans m;
			m.scale(sx,sy);
			e->map(m);
		}
		else if (sl[i].left(6)=="rotate"){
			double s=1;
			int x=0;
			int y=0;
			if (para.size()>0) s=(para[0].toDouble());
			if (para.size()>1) x=element::runden(para[1].toDouble()/df->userunits);
			if (para.size()>2) y=element::runden(-para[2].toDouble()/df->userunits);
			strans m;
			//printf("angle0 %f\n",m.angle);
			m.translate(x,y);
    			m.rotate(-s);
			//printf("angle1 %f\n",m.angle);
    			m.translate(-x,-y);
			e->map(m);
		}
		else if (sl[i].left(6)=="matrix"){
			double d[6];
			for (int k=0;k<6;k++){ 
				d[k]=0.0; 
				if (para.size()>0) d[k]=(para[k].toDouble());
				//printf("matrix %d %f\n",k,d[k]);
				};
			strans m;
			m.matrix.setMatrix(d[0],-d[1],-d[2],d[3], element::runden(d[4]/df->userunits),-element::runden(d[5]/df->userunits));
			//m.scale(x,y);
			//m.rotate(-s);
    			//m.translate( element::runden(d[4]/df->userunits), -element::runden(d[5]/df->userunits));
			e->map(m);
		}
	/*	else if (sl[i].left(5)=="skewY"){
			double s=1;
			if (para.size()>0) s=(para[0].toDouble());
			strans m;
			m.scale(1.0,s);
			e->map(m);
		}
		else if (sl[i].left(5)=="skewX"){
			double s=1;
			if (para.size()>0) s=(para[0].toDouble());
			strans m;
			m.scale(s,1.0);
			e->map(m);
		}*/
            	else {
                  report.addItem(tr("unknow/unsupported transformation"),2,sl[i]);
		}
	}
	/*for (int i=0;i<sl.size();i++){
		printf("transform translate %s ->%s\n",sl[i].toAscii().data(),sl[i].section("(",1).toAscii().data());
		QStringList para=sl[i].section("(",1).split(QRegExp("[,()a-d f-z\n\r\t]{1,1000}"),QString::SkipEmptyParts);
		for (int k=0;k<para.size();k++)printf("para %d: %s\n",k,para[k].toAscii().data());
		if (sl[i].left(9)=="translate"){
			int x=0;
			int y=0;
			if (para.size()>0) x=element::runden(para[0].toDouble()/df->userunits);
			if (para.size()>1) y=element::runden(-para[1].toDouble()/df->userunits);
			e->move(QPoint(x,y));
		}
		}*/
}

void svg::readEnd(bool ignore){
    while (!xml->atEnd()) {
        xml->readNext();

        if (xml->isEndElement())
            break;

        if (xml->isStartElement()) {
                if (!ignore)report.addItem(tr("unknow/unsupported subelement"),3,xml->name().toString());
		readEnd(ignore);
        }
    }
}

void svg::setWidth(element *e){
QString width="";
	if (xml->attributes().value("stroke-width")!="") width=xml->attributes().value("stroke-width").toString();
else if (xml->attributes().value("style")!="")  {
	QString s=xml->attributes().value("style").toString();
	QStringList sl=s.split(QRegExp("[; ,\t]"));
	for (int i=0;i<sl.size();i++){
		sl[i].remove(":");
		sl[i].remove("=");
		int k=sl[i].indexOf("stroke-width");
		if (k>=0) width=sl[i].mid(k+12);
		}
	}

if (width=="") return;
if (width.contains("px")) e->setWidth(-width.toInt());
else e->setWidth(element::runden(width.toDouble()/df->userunits));
}

void svg::setCap(element *e){
QString width="";
/*	if (xml->attributes().value("stroke-width")!="") width=xml->attributes().value("stroke-width").toString();
else*/ 
if (xml->attributes().value("style")!="")  {
	QString s=xml->attributes().value("style").toString();
	QStringList sl=s.split(QRegExp("[; ,\t]"));
	for (int i=0;i<sl.size();i++){
		sl[i].remove(":");
		sl[i].remove("=");
		int k=sl[i].indexOf("stroke-linecap");
		if (k>=0) width=sl[i].mid(k+14);
		}
	}
if (width=="") return;
if (width.contains("round")) e->setCap(1);
if (width.contains("square")) e->setCap(2); //??? ob so correct
}

pointArray svg::getPoints(QString p){
pointArray pa;
QStringList sl=p.split(QRegExp("[ ,\n\r\t]{1,1000}"),QString::SkipEmptyParts);
for (int i=0;i+1<sl.size();i=i+2){
	//printf(" %s %s\n",sl[i].toAscii().data(),sl[i+1].toAscii().data());
	pa<<QPoint(element::runden(sl[i].toDouble()/df->userunits),-element::runden(sl[i+1].toDouble()/df->userunits));
}
return pa;
}

int svg::getLayer(){
QString c;
if ((xml->attributes().value("fill")!="")&&(xml->attributes().value("fill").toString().left(4)!="none"))
 return getColor(xml->attributes().value("fill").toString());
if ((xml->attributes().value("stroke")!="")&&(xml->attributes().value("stroke").toString().left(4)!="none"))
 return getColor(xml->attributes().value("stroke").toString());
if (xml->attributes().value("style")=="")return 0;
QString s=xml->attributes().value("style").toString();
int k=s.indexOf("fill:");
if (k>=0) {
	int j=s.indexOf(";",k+1);
	QString s2=s.mid(k+5,j-k-5);
	if (s2.trimmed().left(4)!="none")return getColor(s2);
	}
k=s.indexOf("stroke:");
if (k>=0) {
	int j=s.indexOf(";",k+1);
	s=s.mid(k+7,j-k-7);
	return getColor(s);
	}
return 0;
}

int svg::getColor(QString s){
if (s.contains("rgb")){
	int i=s.indexOf("(");
	int k=s.indexOf(")",i);
	if ((i>0)&&(k>0)) s=s.mid(i,k-i-1);
	QStringList sl=s.split(",");
	if (sl.size()==3){
		int r=0;
		int b=0;
		int g=0;
		if (sl.at(0).contains("%")) 
			{sl[0].remove("%"); 
			r=(int)(sl[0].toDouble()*2.54);
			}else r=sl[0].toInt();
		if (sl.at(1).contains("%")) 
			{sl[1].remove("%"); 
			g=(int)(sl[1].toDouble()*2.54);
			}else g=sl[1].toInt();
		if (sl.at(2).contains("%")) 
			{sl[2].remove("%"); 
			b=(int)(sl[2].toDouble()*2.54);
			}else b=sl[2].toInt();
		return layers::bestColor(QColor(r,g,b));
		}
	
	}
QColor col;
col.setNamedColor(s);
//printf("%s %d %d %d\n",col.name().toAscii().data(),col.red(),col.green(),col.blue());
return layers::bestColor(col);
}

int svg::getInt(QStringRef s){
//printf("%s -> %d\n",s.toString().toAscii().data(),element::runden(s.toString().toDouble()/df->userunits));
return element::runden(s.toString().toDouble()/df->userunits);
}

void svg::save(QString fileName,drawingField *d){
errorreport report;
report.setTitle(tr("Save of SVG-File \"")+fileName+"\"");
try { 
 QFile f( fileName );
  if ( !f.open( QIODevice::WriteOnly ) ) {
	throw QString(tr("Can not open File."));
  }
    QTextStream stream( &f );
  svg s;
  s.error=&report;
  s.save(d,&stream);
  f.close();
}
catch (QString s){
  report.addItem(tr("Aborted. "),0);
  report.addItem(s,1);
 }
  //report.showReport();   
  QString s=report.getReport();
  d->showReport(s,report.getLastRang());
}

void svg::save(drawingField *d,QTextStream *stream){
  if (setup::svgEndOfLine==1)endOfLine="\n";
  else endOfLine="\r\n";
  df=d;
  str=stream;
  cellList *e;
  cell *topcell;
  topcell=d->findTopCell();
  QPoint min,max;
  uint count;
  topcell->paintInfoGet(&min,&max,&count);
  for(e=d->firstCell;e!=NULL;e=e->nextCell){
	e->thisCell->saved=false;}

  *str<<"<?xml version=\"1.0\" encoding=\"ISO-8859-1\" standalone=\"no\"?>"<<endOfLine;
  *str<<"<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 20010904//EN\""<<endOfLine;
  *str<<"\"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\">"<<endOfLine;
  *str<<"<svg xmlns=\"http://www.w3.org/2000/svg\""<<endOfLine;
  *str<<"xmlns:xlink=\"http://www.w3.org/1999/xlink\""<<endOfLine;
  savePos("x",min.x());
  savePos("y",-max.y());
  savePos("width",max.x()-min.x());
  savePos("height",max.y()-min.y());
  QString vb=convertPos(min.x())+" "+convertPos(-max.y())+" "+convertPos(max.x()-min.x())+" "+convertPos(max.y()-min.y());
  saveValue("viewBox",vb);
  if ((max.y()-min.y()<20)|| (max.x()-min.x()<20))  error->addItem("The resulting graphic is quite small. A scaling can be required to see it with some viewers. Decrease the user units to get a larger image.",4);
  if ((max.y()-min.y()>2000)|| (max.x()-min.x()>2000))  error->addItem("The resulting graphic is quite large. A scaling can be required to see the hole design with some viewers. Increase the user units to get a smaller image.",4);
  *str<<">\r\n";
  *str<<"<title>"+topcell->cellName+"</title>"<<endOfLine;
  *str<<"<defs>"<<endOfLine;
  bool saved=false;
  topcell->saved=true;
  while (!saved){
	saved=true;
	for(e=d->firstCell;e!=NULL;e=e->nextCell){
		if (e->thisCell->saved==false){
			if (!e->thisCell->dependNotSaved()){
				*str<<"<symbol id=\""+e->thisCell->cellName+"\" >"<<endOfLine;
				e->thisCell->saveSVG(this);
				*str<<"</symbol>"<<endOfLine;
				}
			else{saved=false;}};
		}}
  *str<<"</defs>"<<endOfLine;
  if ((min.x()<0)||(max.y()>0)) error->addItem("Some elements are outside the 4.quadrant and may be invisible by some viewer. Move the cell orgin to make it visible with all viewers",4);
  topcell->saved=false;
  topcell->saveSVG(this);
  *str<<"</svg>"<<endOfLine;
}

QString svg::convertPos(int i){
return convertNum(((double)(i))*df->userunits);
}

void svg::savePos(QString s1,int i){
double d=((double)(i))*df->userunits;
saveNum(s1,d);
}

QString svg::convertNum(double i){
QString s;
	s.setNum(i,'f',6);
	if (s.contains("."))
		while (s[s.size()-1]=='0') {
			s=s.left(s.size()-1);
		}
	while (s[s.size()-1]=='.') {
		s=s.left(s.size()-1);
	}
return s;
}

void svg::saveNum(QString s1,double i){
	/*QString s;
	s.setNum(i,'f',6);
	if (s.contains("."))
		while (s[s.size()-1]=='0') {
			s=s.left(s.size()-1);
		}
	while (s[s.size()-1]=='.') {
		s=s.left(s.size()-1);
	}*/
	saveValue(s1,convertNum(i));
}

void svg::saveValue(QString s1,QString s2){
if (s1!="")
	*str<<s1<<"=\""<<s2<<"\" ";
else 
	*str<<s2<<" ";
}


