/***************************************************************************
 *   Copyright (C) 2004 by Hans Oischinger                                 *
 *   hans.oischinger@kdemail.net                                           *
 *                                                                         *
 *   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.,                                       *
 *   51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301, USA.             *
 ***************************************************************************/
#include "svgexportvisitor.h"

// Core
#include "core/direktorinstance.h"
#include "core/elementdatabase.h"

// Elements
#include "elements/vectorelement.h"
#include "elements/documentelement.h"
#include "elements/animatedlengthattr.h"
#include "elements/lengthattribute.h"
#include "elements/animatedcolorattr.h"
#include "elements/colorattribute.h"
#include "elements/fillattribute.h"
#include "elements/strokeattribute.h"
#include "elements/vectorpath.h"
#include "elements/groupelement.h"
#include "elements/useelement.h"
#include "elements/matrix.h"
#include "elements/elementselection.h"
#include "elements/elementtreeiterator.h"
#include "elements/animatednumber.h"
#include "elements/number.h"
#include "elements/lineargradient.h"
#include "elements/radialgradient.h"
#include "elements/urireference.h"
#include "elements/stopelement.h"
#include "elements/transformlist.h"
#include "elements/animatedtransformlist.h"
#include "elements/enumeration.h"
#include "elements/animatedenum.h"
#include "elements/defselement.h"
#include "elements/animateelement.h"
#include "elements/timeattribute.h"

// Visitors
#include "svgserializer.h"

// Qt
#include <qfile.h>
#include <qtextstream.h>
#include <qcolor.h>

// KDE
#include <kdebug.h>

// KCanvas
#include <kcanvas/KCanvasPath.h>

SVGExportVisitor::SVGExportVisitor(QFile& file)
    : Visitor(),
    m_level(0)
{
  if (file.open(IO_WriteOnly) == false)
    return;

  m_attrser = new SVGSerializer();

  kdDebug() << "SVGExportVisitor::SVGExportVisitor() - Exporting to SVG (file: " << file.name() << endl;
  m_stream = new QTextStream(&file);

  *m_stream << "<?xml version=\"1.0\" standalone=\"no\"?>" << endl;
  *m_stream << "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">" << endl;
  *m_stream << "<svg>" << endl;

  ElementTreeIterator itr( &Kast::self()->elementDB().documentRoot(), true );
  unsigned int level = 0;

  while (itr.current())
  {
    m_level = itr.level();
    if ( itr.current()->getState() != Element::deleted )
      itr.current()->accept( *this );
    itr.next();

    // Pop the closing tags from the stack
    kdDebug() << itr.level() << endl;
    if ( level < itr.level() )
      level = itr.level();
    for (; level > itr.level(); --level)
    {
      *m_stream  << QString().fill( ' ', level-1 );
      *m_stream << m_stack.pop() << endl;
    }
  }

  *m_stream << "</svg>" << endl;
  file.close();
}

SVGExportVisitor::~SVGExportVisitor()
{
  delete m_stream;
  delete m_attrser;
}

bool SVGExportVisitor::visit( VectorElement & element )
{
  *m_stream  << QString().fill( ' ', 2*m_level ) << "<";
  QString tagName;

  switch( element.vectorPath().shapeMetaType() )
  {
  case VectorPath::SHAPE_LINE:
    tagName = "line";
    *m_stream << tagName << " ";
    if ( element.vectorPath().getX1().baseVal().isValid() )
    {
      *m_stream << "x1=\"" << element.vectorPath().getX1().baseVal().value();
      *m_stream << element.vectorPath().getX1().baseVal().readableSpecifiedUnits() << "\" ";
    }
    if ( element.vectorPath().getY1().baseVal().isValid() )
    {
      *m_stream << "y1=\"" << element.vectorPath().getY1().baseVal().value();
      *m_stream << element.vectorPath().getY1().baseVal().readableSpecifiedUnits() << "\" ";
    }
    if ( element.vectorPath().getX2().baseVal().isValid() )
    {
      *m_stream << "x2=\"" << element.vectorPath().getX2().baseVal().value();
      *m_stream << element.vectorPath().getX2().baseVal().readableSpecifiedUnits() << "\" ";
    }
    if ( element.vectorPath().getY2().baseVal().isValid() )
    {
      *m_stream << "y2=\"" << element.vectorPath().getY2().baseVal().value();
      *m_stream << element.vectorPath().getY2().baseVal().readableSpecifiedUnits() << "\" ";
    }
    break;
  case VectorPath::SHAPE_ELLIPSE:
    tagName = "ellipse";
    *m_stream << tagName << " ";
    if ( element.vectorPath().getRadiusX().baseVal().isValid() )
    {
      *m_stream << "cx=\"" << element.vectorPath().getRadiusX().baseVal().value();
      *m_stream << element.vectorPath().getRadiusX().baseVal().readableSpecifiedUnits() << "\" ";
    }
    if ( element.vectorPath().getRadiusY().baseVal().isValid() )
    {
      *m_stream << "cy=\"" << element.vectorPath().getRadiusY().baseVal().value();
      *m_stream << element.vectorPath().getRadiusY().baseVal().readableSpecifiedUnits() << "\" ";
    }
    if ( element.vectorPath().getRX().baseVal().isValid() )
    {
      *m_stream << "rx=\"" << element.vectorPath().getRX().baseVal().value();
      *m_stream << element.vectorPath().getRX().baseVal().readableSpecifiedUnits() << "\" ";
    }
    if ( element.vectorPath().getRY().baseVal().isValid() )
    {
      *m_stream << "ry=\"" << element.vectorPath().getRY().baseVal().value();
      *m_stream << element.vectorPath().getRY().baseVal().readableSpecifiedUnits() << "\" ";
    }
    break;
  case VectorPath::SHAPE_RRECT:
  case VectorPath::SHAPE_RECT:
    tagName = "rect";
    *m_stream << tagName << " ";
    if ( element.vectorPath().getX().baseVal().isValid() )
    {
      *m_stream << "x=\"" << element.vectorPath().getX().baseVal().value();
      *m_stream << element.vectorPath().getX().baseVal().readableSpecifiedUnits() << "\" ";
    }
    if ( element.vectorPath().getY().baseVal().isValid() )
    {
      *m_stream << "y=\"" << element.vectorPath().getY().baseVal().value();
      *m_stream << element.vectorPath().getY().baseVal().readableSpecifiedUnits() << "\" ";
    }
    if ( element.vectorPath().getWidth().baseVal().isValid() )
    {
      *m_stream << "width=\"" << element.vectorPath().getWidth().baseVal().value();
      *m_stream << element.vectorPath().getWidth().baseVal().readableSpecifiedUnits() << "\" ";
    }
    if ( element.vectorPath().getHeight().baseVal().isValid() )
    {
      *m_stream << "height=\"" << element.vectorPath().getHeight().baseVal().value();
      *m_stream << element.vectorPath().getHeight().baseVal().readableSpecifiedUnits() << "\" ";
    }
    if ( element.vectorPath().getRX().baseVal().isValid() )
    {
      *m_stream << "rx=\"" << element.vectorPath().getRX().baseVal().value();
      *m_stream << element.vectorPath().getRX().baseVal().readableSpecifiedUnits() << "\" ";
    }
    if ( element.vectorPath().getRY().baseVal().isValid() )
    {
      *m_stream << "ry=\"" << element.vectorPath().getRY().baseVal().value();
      *m_stream << element.vectorPath().getRY().baseVal().readableSpecifiedUnits() << "\" ";
    }
    break;
  case VectorPath::SHAPE_CIRCLE:
    tagName = "circle";
    *m_stream << tagName << " ";
    if ( element.vectorPath().getRadiusX().baseVal().isValid() )
    {
      *m_stream << "cx=\"" << element.vectorPath().getRadiusX().baseVal().value();
      *m_stream << element.vectorPath().getRadiusX().baseVal().readableSpecifiedUnits() << "\" ";
    }
    if ( element.vectorPath().getRadiusY().baseVal().isValid() )
    {
      *m_stream << "cy=\"" << element.vectorPath().getRadiusY().baseVal().value();
      *m_stream << element.vectorPath().getRadiusY().baseVal().readableSpecifiedUnits() << "\" ";
    }
    if ( element.vectorPath().getRadius().baseVal().isValid() )
    {
      *m_stream << "r=\"" << element.vectorPath().getRadius().baseVal().value();
      *m_stream << element.vectorPath().getRadius().baseVal().readableSpecifiedUnits() << "\" ";
    }
    break;
  case VectorPath::SHAPE_POLYGON:
  case VectorPath::SHAPE_POLYLINE:
  case VectorPath::SHAPE_PATH:
    {
      tagName = "path";
      *m_stream << tagName << " ";
      *m_stream << "d=\"";

      KCPathDataList::const_iterator it;
      for ( it = element.vectorPath().kcPathData()->begin(); it != element.vectorPath().kcPathData()->end(); ++it )
      {
        switch( (*it).cmd )
        {
        case CMD_MOVE:
          *m_stream << " M" << (*it).x3 << "," << (*it).y3;
          break;
        case CMD_LINE:
          *m_stream << " L" << (*it).x3 << "," << (*it).y3;
          break;
        case CMD_CURVE:
          *m_stream << " C" << (*it).x1 << "," << (*it).y1;
          *m_stream << " " << (*it).x2 << "," << (*it).y2;
          *m_stream << " " << (*it).x3 << "," << (*it).y3;
          break;
        case CMD_CLOSE_SUBPATH:
          *m_stream << " Z";
          break;
        default:
          break;
        }
      }

      *m_stream << "\" ";
    }
    break;
  default:
    break;
  }

  processElementNode(element);
  processTransformable( element );
  processStylable( element );

  if ( element.hasChildNodes() )
  {
    *m_stream << ">" << endl;
    m_stack.push( QString("</").append(tagName).append(">") );
  }
  else
    *m_stream << "/>" << endl;
  return true;
}

bool SVGExportVisitor::visit( GroupElement & element )
{
  //   kdDebug() << k_funcinfo << endl;
  *m_stream  << QString().fill( ' ', 2*m_level ) << "<";
  *m_stream << "g ";
  processElementNode(element);
  processTransformable(element);

  if ( element.hasChildNodes() )
  {
    *m_stream << ">" << endl;
    m_stack.push( "</g>" );
  }
  else
    *m_stream << "/>" << endl;
  return true;
}

bool SVGExportVisitor::visit( DefsElement & element )
{
  //   kdDebug() << k_funcinfo << endl;
  *m_stream  << QString().fill( ' ', 2*m_level ) << "<";
  *m_stream << "defs ";
  processElementNode(element);
  processTransformable(element);

  if ( element.hasChildNodes() )
  {
    *m_stream << ">" << endl;
    m_stack.push( "</defs>" );
  }
  else
    *m_stream << "/>" << endl;
  return true;
}

bool SVGExportVisitor::visit( UseElement & element )
{
  //   kdDebug() << k_funcinfo << endl;
  *m_stream  << QString().fill( ' ', 2*m_level ) << "<";
  *m_stream << "use " << endl;
  processElementNode(element);
  processTransformable(element);

  *m_stream  << "xlink:href=\"" << element.ref().getURI() << "\" />";
  return true;
}

bool SVGExportVisitor::visit( StopElement & element )
{
  *m_stream  << QString().fill( ' ', 2*m_level ) << "<";
  *m_stream << "stop ";
  processElementNode(element);
  *m_stream << "offset=\"" << element.getOffset().baseVal().value() << "\" ";
  *m_stream << "stop-color=\"" << element.getStopColor().baseVal().qColor().name() << "\" ";
  *m_stream << "stop-opacity=\"" << element.getStopOpacity().baseVal().value() << "\" ";
  *m_stream << "/>" << endl;
  return true;
}

bool SVGExportVisitor::visit( LinearGradient & element )
{
  *m_stream  << QString().fill( ' ', 2*m_level ) << "<";
  *m_stream << "linearGradient ";
  processElementNode(element);
  processGradient(element);

  if ( element.getX1().baseVal().isValid() )
  {
    *m_stream << "x1=\"" << element.getX1().baseVal().value();
    *m_stream << element.getX1().baseVal().readableSpecifiedUnits() << "\" ";
  }
  if ( element.getY1().baseVal().isValid() )
  {
    *m_stream << "y1=\"" << element.getY1().baseVal().value();
    *m_stream << element.getY1().baseVal().readableSpecifiedUnits() << "\" ";
  }
  if ( element.getX2().baseVal().isValid() )
  {
    *m_stream << "x2=\"" << element.getX2().baseVal().value();
    *m_stream << element.getX2().baseVal().readableSpecifiedUnits() << "\" ";
  }
  if ( element.getY2().baseVal().isValid() )
  {
    *m_stream << "y2=\"" << element.getY2().baseVal().value();
    *m_stream << element.getY2().baseVal().readableSpecifiedUnits() << "\" ";
  }

  if ( element.hasChildNodes() )
  {
    *m_stream << ">" << endl;
    m_stack.push( "</linearGradient>" );
  }
  else
    *m_stream << "/>" << endl;
  return true;
}

bool SVGExportVisitor::visit( RadialGradient & element )
{
  *m_stream  << QString().fill( ' ', 2*m_level ) << "<";
  *m_stream << "radialGradient ";
  processElementNode(element);
  processGradient(element);

  if ( element.getCX().baseVal().isValid() )
  {
    *m_stream << "cx=\"" << element.getCX().baseVal().value();
    *m_stream << element.getCX().baseVal().readableSpecifiedUnits() << "\" ";
  }
  if ( element.getCY().baseVal().isValid() )
  {
    *m_stream << "cy=\"" << element.getCY().baseVal().value();
    *m_stream << element.getCY().baseVal().readableSpecifiedUnits() << "\" ";
  }
  if ( element.getR().baseVal().isValid() )
  {
    *m_stream << "r=\"" << element.getR().baseVal().value();
    *m_stream << element.getR().baseVal().readableSpecifiedUnits() << "\" ";
  }
  if ( element.getFX().baseVal().isValid() )
  {
    *m_stream << "fx=\"" << element.getFX().baseVal().value();
    *m_stream << element.getFX().baseVal().readableSpecifiedUnits() << "\" ";
  }
  if ( element.getFY().baseVal().isValid() )
  {
    *m_stream << "fy=\"" << element.getFY().baseVal().value();
    *m_stream << element.getFY().baseVal().readableSpecifiedUnits() << "\" ";
  }

  if ( element.hasChildNodes() )
  {
    *m_stream << ">" << endl;
    m_stack.push( "</radialGradient>" );
  }
  else
    *m_stream << "/>" << endl;
  return true;
}

bool SVGExportVisitor::visit( AnimateTransformElement & element )
{
  *m_stream  << QString().fill( ' ', 2*m_level ) << "<";
  *m_stream << "animateTransform ";

  *m_stream  << "attributeName=\"" << m_attrser->serializeAttribute( *element.ref().getRefElement(), element.targetAttribute() ) << "\" ";

  processAnimateElement(element);

  *m_stream << "additive=\"sum\" " << endl;

  if ( element.toAttribute().isValid() )
    *m_stream << "type=\"" << element.toAttribute().getReadableTransformMetaType() <<  "\" ";
  else
    if ( element.byAttribute().isValid() )
      *m_stream << "type=\"" << element.byAttribute().getReadableTransformMetaType() <<  "\" ";

  if ( element.fromAttribute().isValid() )
    *m_stream << "from=\"" << serializeTransformContents( element.fromAttribute() ) <<  "\" ";
  else
  {
    if ( element.toAttribute().transformMetaType() == Transform::TRANSFORM_SCALE ||
         element.byAttribute().transformMetaType() == Transform::TRANSFORM_SCALE )
      *m_stream << "from=\"1\" ";   // adobe svg plugin only works with fromm Attr set!!!
    else
      *m_stream << "from=\"0\" ";   // adobe svg plugin only works with fromm Attr set!!!
  }

  if ( element.toAttribute().isValid() )
    *m_stream << "to=\"" << serializeTransformContents( element.toAttribute() ) <<  "\" ";
  if ( element.byAttribute().isValid() )
    *m_stream << "to=\"" << serializeTransformContents( element.byAttribute() ) <<  "\" ";

  *m_stream << "/>" << endl;
  return true;
}

bool SVGExportVisitor::visit( AnimateLengthElement & element )
{
  *m_stream  << QString().fill( ' ', 2*m_level ) << "<";
  *m_stream << "animate ";

  *m_stream  << "attributeName=\"" << m_attrser->serializeAttribute( *element.ref().getRefElement(), element.targetAttribute() ) << "\" ";

  processAnimateElement(element);
  // TODO: finsh
  *m_stream << "/>" << endl;
  return true;
}

bool SVGExportVisitor::visit( AnimateNumberElement & element )
{
  *m_stream  << QString().fill( ' ', 2*m_level ) << "<";
  *m_stream << "animate ";

  *m_stream  << "attributeName=\"" << m_attrser->serializeAttribute( *element.ref().getRefElement(), element.targetAttribute() ) << "\" ";

  processAnimateElement(element);
  // TODO: finsh
  *m_stream << "/>" << endl;
  return true;
}











bool SVGExportVisitor::processStylable( Stylable & element )
{
  *m_stream  << "style=\"";
  {
    switch(element.fill().fillType())
    {
    case FillAttribute::FILL_UNSPECIFIED:
      break;
    case FillAttribute::FILL_NONE:
      *m_stream  << "fill:none;";
      break;
    case FillAttribute::FILL_SOLID:
      *m_stream  << "fill:" << element.fill().color().baseVal().qColor().name() << ";";
      break;
    case FillAttribute::FILL_REF:
      *m_stream  << "fill:url(" << element.fill().ref().getURI() << ");";
      break;
    default:
      break;
    }
    if ( element.fill().fillOpacity().isValid() )
      *m_stream  << "fill-opacity:" << element.fill().fillOpacity().baseVal().value() << ";";


    switch(element.stroke().strokeType())
    {
    case StrokeAttribute::STROKE_UNKNOWN:
      break;
    case StrokeAttribute::STROKE_NONE:
      *m_stream  << "stroke:none;";
      break;
    case StrokeAttribute::STROKE_SOLID:
      *m_stream  << "stroke:" << element.stroke().color().baseVal().qColor().name() << ";";
      break;
    case StrokeAttribute::STROKE_REF:
      *m_stream  << "stroke:url(" << element.stroke().ref().getURI() << ");";
      break;
    default:
      break;
    }
    if ( element.stroke().strokeOpacity().isValid() )
      *m_stream  << "stroke-opacity:" << element.stroke().strokeOpacity().baseVal().value() << ";";
    if ( element.stroke().strokeWidth().isValid() )
      *m_stream  << "stroke-width:" << element.stroke().strokeWidth().baseVal().value() << ";";
  }
  *m_stream  << "\" ";
  return true;
}

bool SVGExportVisitor::processTransformable( Transformable & element )
{
  if ( element.transformList().baseVal().isValid() )
    *m_stream  << "transform=\"" << serializeTransformList( element.transformList().baseVal() ) << "\" ";
  return true;
}

bool SVGExportVisitor::processGradient( Gradient & element )
{
  if ( element.gradientUnits().baseVal().isValid() )
  {
    *m_stream  << "gradientUnits=\"";
    switch(element.gradientUnits().baseVal().value())
    {
    case Gradient::GRADUNITS_OBJECTBOUNDINGBOX:
      *m_stream  << "objectBoundingBox";
      break;
    case Gradient::GRADUNITS_USERSPACEONUSE:
      *m_stream  << "userSpaceOnUse";
      break;
    default:
      break;
    }
    *m_stream  << "\" ";
  }

  if ( element.gradientTransformList().baseVal().isValid() )
    *m_stream  << "gradientTransform=\"" << serializeTransformList( element.gradientTransformList().baseVal() ) << "\" ";

  if ( element.spreadMethod().baseVal().isValid() )
  {
    *m_stream  << "spreadMethod=\"";
    switch(element.spreadMethod().baseVal().value())
    {
    case Gradient::SPREADMETHOD_PAD:
      *m_stream  << "pad";
      break;
    case Gradient::SPREADMETHOD_REFLECT:
      *m_stream  << "reflect";
      break;
    case Gradient::SPREADMETHOD_REPEAT:
      *m_stream  << "repeat";
      break;
    case Gradient::SPREADMETHOD_UNKNOWN:
      break;
    default:
      break;
    }
    *m_stream  << "\" ";
  }

  *m_stream  << "xlink:href=\"" << element.ref().getURI() << "\" ";
  return true;
}

bool SVGExportVisitor::processElementNode( ElementNode & element )
{
  if ( !element.id().isEmpty() )
    *m_stream  << "id=\"" << element.id() << "\" ";
  return true;
}

bool SVGExportVisitor::processAnimateElement( AnimateElement & element )
{
  if ( element.ref().isValid() )
    *m_stream  << "xlink:href=\"" << element.ref().getURI() << "\" ";
  if ( element.getStartTime().isValid() )
    *m_stream  << "begin=\"" << element.getStartTime().value() << "s\" ";
  if ( element.getSimpleDuration().isValid() )
    *m_stream  << "dur=\"" << element.getSimpleDuration().value() << "s\" ";

  *m_stream  << "fill=\"";
  switch ( element.getFill() )
  {
  case AnimateElement::FILL_FREEZE:
    *m_stream  << "freeze";
    break;
  case AnimateElement::FILL_REMOVE:
    *m_stream  << "remove";
    break;
  }
  *m_stream  << "\" ";

  return true;
}

const QString SVGExportVisitor::serializeTransformList( const TransformList &t ) const
{
  QString tString;
  QTextStream tStream( &tString, IO_WriteOnly );
  QPtrListIterator<Transform> it( t );
  Transform *transform;
  while ( (transform = it.current()) != 0 )
  {
    ++it;

    switch( transform->transformMetaType() )
    {
    case Transform::TRANSFORM_ROTATE:
      tStream << "rotate(" << transform->getRotateAngle();
      // if ( transform->getRotateCX().isValid() )  // FIXME: make all these doubles animatable
      tStream << " " << transform->getRotateCX() << " " << transform->getRotateCY();
      tStream << ") ";
      break;
    case Transform::TRANSFORM_TRANSLATE:
      tStream << "translate(" << transform->getTranslateX() << " " << transform->getTranslateY() << ") ";
      break;
    case Transform::TRANSFORM_SCALE:
      tStream << "scale(" << transform->getScaleX();
      //if ( transform->getScaleY().isValid() )
      tStream << " " << transform->getScaleY();
      tStream << ") ";
      break;
    case Transform::TRANSFORM_SKEWX:
      tStream << "skewX(" << transform->getSkewX() << ") ";
      break;
    case Transform::TRANSFORM_SKEWY:
      tStream << "skewY(" << transform->getSkewY() << ") ";
      break;
    case Transform::TRANSFORM_MATRIX:
      tStream << "matrix(" << transform->matrix()->a() << " " << transform->matrix()->b() << " " << transform->matrix()->c() << " " << transform->matrix()->d() << " " << transform->matrix()->e() << " " << transform->matrix()->f() << ") ";
      break;
    default:
      break;
    }
  }
  return tString;
}

const QString SVGExportVisitor::serializeTransformContents( const Transform& transform ) const
{
  QString tString;
  QTextStream tStream( &tString, IO_WriteOnly );
  switch( transform.transformMetaType() )
  {
  case Transform::TRANSFORM_ROTATE:
    tStream << transform.getRotateAngle();
    // if ( transform.getRotateCX().isValid() )  // FIXME: make all these doubles animatable
    tStream << " " << transform.getRotateCX() << " " << transform.getRotateCY();
    break;
  case Transform::TRANSFORM_TRANSLATE:
    tStream << transform.getTranslateX() << " " << transform.getTranslateY();
    break;
  case Transform::TRANSFORM_SCALE:
    tStream << transform.getScaleX();
    //if ( transform.getScaleY().isValid() )
    tStream << " " << transform.getScaleY();
    break;
  case Transform::TRANSFORM_SKEWX:
    tStream << transform.getSkewX();
    break;
  case Transform::TRANSFORM_SKEWY:
    tStream << transform.getSkewY();
    break;
  case Transform::TRANSFORM_MATRIX:
    tStream << transform.matrix()->a() << " " << transform.matrix()->b() << " " << transform.matrix()->c() << " " << transform.matrix()->d() << " " << transform.matrix()->e() << " " << transform.matrix()->f();
    break;
  default:
    break;
  }
  return tString;
}
