summaryrefslogtreecommitdiffstats
path: root/tdecore/svgicons
diff options
context:
space:
mode:
Diffstat (limited to 'tdecore/svgicons')
-rw-r--r--tdecore/svgicons/CMakeLists.txt38
-rw-r--r--tdecore/svgicons/Makefile.am6
-rw-r--r--tdecore/svgicons/TODO5
-rw-r--r--tdecore/svgicons/ksvgiconengine.cpp692
-rw-r--r--tdecore/svgicons/ksvgiconengine.h49
-rw-r--r--tdecore/svgicons/ksvgiconpainter.cpp2837
-rw-r--r--tdecore/svgicons/ksvgiconpainter.h101
7 files changed, 3728 insertions, 0 deletions
diff --git a/tdecore/svgicons/CMakeLists.txt b/tdecore/svgicons/CMakeLists.txt
new file mode 100644
index 000000000..4f2345510
--- /dev/null
+++ b/tdecore/svgicons/CMakeLists.txt
@@ -0,0 +1,38 @@
+#################################################
+#
+# (C) 2010 Serghei Amelian
+# serghei (DOT) amelian (AT) gmail.com
+#
+# Improvements and feedback are welcome
+#
+# This file is released under GPL >= 2
+#
+#################################################
+
+include_directories(
+ ${TQT_INCLUDE_DIRS}
+ ${CMAKE_BINARY_DIR}/tdecore
+ ${CMAKE_SOURCE_DIR}/tdecore
+ ${LIBART_INCLUDE_DIRS}
+)
+
+
+##### headers ###################################
+
+install( FILES
+ ksvgiconengine.h
+ DESTINATION ${INCLUDE_INSTALL_DIR} )
+
+
+##### kdesvgicons ###############################
+
+set( target kdesvgicons )
+
+set( ${target}_SRCS
+ ksvgiconengine.cpp ksvgiconpainter.cpp
+)
+
+tde_add_library( ${target} STATIC_PIC
+ SOURCES ${${target}_SRCS}
+ LINK ${LIBART_LIBRARIES}
+)
diff --git a/tdecore/svgicons/Makefile.am b/tdecore/svgicons/Makefile.am
new file mode 100644
index 000000000..ca398a5bd
--- /dev/null
+++ b/tdecore/svgicons/Makefile.am
@@ -0,0 +1,6 @@
+INCLUDES = -I$(srcdir)/.. $(LIBART_CFLAGS) $(all_includes)
+
+noinst_LTLIBRARIES = libkdesvgicons.la
+
+include_HEADERS = ksvgiconengine.h
+libkdesvgicons_la_SOURCES = ksvgiconengine.cpp ksvgiconpainter.cpp
diff --git a/tdecore/svgicons/TODO b/tdecore/svgicons/TODO
new file mode 100644
index 000000000..6ecafb8bf
--- /dev/null
+++ b/tdecore/svgicons/TODO
@@ -0,0 +1,5 @@
+* Units.svg is wrong
+* faster implementations of parseStyle, parseTransform, if needed
+* smaller art_render_new areas, if needed
+* make gradients work for boundingbox as well
+* patterns
diff --git a/tdecore/svgicons/ksvgiconengine.cpp b/tdecore/svgicons/ksvgiconengine.cpp
new file mode 100644
index 000000000..85830126b
--- /dev/null
+++ b/tdecore/svgicons/ksvgiconengine.cpp
@@ -0,0 +1,692 @@
+/*
+ Copyright (C) 2002 Nikolas Zimmermann <wildfox@kde.org>
+ This file is part of the KDE project
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include <tqdom.h>
+#include <tqfile.h>
+#include <tqcolor.h>
+#include <tqimage.h>
+#include <tqwmatrix.h>
+#include <tqregexp.h>
+
+#include <kmdcodec.h>
+
+#include <zlib.h>
+
+#include "ksvgiconpainter.h"
+#include "ksvgiconengine.h"
+
+class KSVGIconEngineHelper
+{
+public:
+ KSVGIconEngineHelper(KSVGIconEngine *engine)
+ {
+ m_engine = engine;
+ }
+
+ ~KSVGIconEngineHelper()
+ {
+ }
+
+ double toPixel(const TQString &s, bool hmode)
+ {
+ return m_engine->painter()->toPixel(s, hmode);
+ }
+
+ ArtGradientStop *parseGradientStops(TQDomElement element, int &offsets)
+ {
+ if (!element.hasChildNodes())
+ return 0;
+
+ TQValueList<ArtGradientStop> stopList;
+
+ float oldOffset = -1, newOffset = -1;
+ for(TQDomNode node = element.firstChild(); !node.isNull(); node = node.nextSibling())
+ {
+ TQDomElement element = node.toElement();
+
+ oldOffset = newOffset;
+ TQString temp = element.attribute("offset");
+
+ if(temp.contains("%"))
+ {
+ temp = temp.left(temp.length() - 1);
+ newOffset = temp.toFloat() / 100.0;
+ }
+ else
+ newOffset = temp.toFloat();
+
+ // Spec skip double offset specifications
+ if(oldOffset == newOffset)
+ continue;
+
+ offsets++;
+ stopList.append(ArtGradientStop());
+
+ ArtGradientStop &stop = stopList.last();
+
+ stop.offset = newOffset;
+
+ TQString parseOpacity;
+ TQString parseColor;
+
+ if(element.hasAttribute("stop-opacity"))
+ parseOpacity = element.attribute("stop-opacity");
+
+ if(element.hasAttribute("stop-color"))
+ parseColor = element.attribute("stop-color");
+
+ if(parseOpacity.isEmpty() || parseColor.isEmpty())
+ {
+ TQString style = element.attribute("style");
+
+ TQStringList substyles = TQStringList::split(';', style);
+ for(TQStringList::Iterator it = substyles.begin(); it != substyles.end(); ++it)
+ {
+ TQStringList substyle = TQStringList::split(':', (*it));
+ TQString command = substyle[0];
+ TQString params = substyle[1];
+ command = command.stripWhiteSpace();
+ params = params.stripWhiteSpace();
+
+ if(command == "stop-color")
+ {
+ parseColor = params;
+
+ if(!parseOpacity.isEmpty())
+ break;
+ }
+ else if(command == "stop-opacity")
+ {
+ parseOpacity = params;
+
+ if(!parseColor.isEmpty())
+ break;
+ }
+ }
+ }
+
+ // Parse color using KSVGIconPainter (which uses Qt)
+ // Supports all svg-needed color formats
+ TQColor qStopColor = m_engine->painter()->parseColor(parseColor);
+
+ // Convert in a libart suitable form
+ TQ_UINT32 stopColor = m_engine->painter()->toArtColor(qStopColor);
+
+ int opacity = m_engine->painter()->parseOpacity(parseOpacity);
+
+ TQ_UINT32 rgba = (stopColor << 8) | opacity;
+ TQ_UINT32 r, g, b, a;
+
+ // Convert from separated to premultiplied alpha
+ a = rgba & 0xff;
+ r = (rgba >> 24) * a + 0x80;
+ r = (r + (r >> 8)) >> 8;
+ g = ((rgba >> 16) & 0xff) * a + 0x80;
+ g = (g + (g >> 8)) >> 8;
+ b = ((rgba >> 8) & 0xff) * a + 0x80;
+ b = (b + (b >> 8)) >> 8;
+
+ stop.color[0] = ART_PIX_MAX_FROM_8(r);
+ stop.color[1] = ART_PIX_MAX_FROM_8(g);
+ stop.color[2] = ART_PIX_MAX_FROM_8(b);
+ stop.color[3] = ART_PIX_MAX_FROM_8(a);
+ }
+
+ if (stopList.isEmpty())
+ return 0;
+
+ ArtGradientStop *stops = new ArtGradientStop[stopList.count()];
+
+ TQValueList<ArtGradientStop>::iterator it = stopList.begin();
+ TQValueList<ArtGradientStop>::iterator end = stopList.end();
+
+ for (int i = 0; it != end; ++i, ++it)
+ stops[i] = *it;
+
+ return stops;
+ }
+
+ TQPointArray parsePoints(TQString points)
+ {
+ if(points.isEmpty())
+ return TQPointArray();
+
+ points = points.simplifyWhiteSpace();
+
+ if(points.contains(",,") || points.contains(", ,"))
+ return TQPointArray();
+
+ points.replace(',', ' ');
+ points.replace('\r', TQString());
+ points.replace('\n', TQString());
+
+ points = points.simplifyWhiteSpace();
+
+ TQStringList pointList = TQStringList::split(' ', points);
+
+ TQPointArray array(pointList.count() / 2);
+ int i = 0;
+
+ for(TQStringList::Iterator it = pointList.begin(); it != pointList.end(); it++)
+ {
+ float x = (*(it++)).toFloat();
+ float y = (*(it)).toFloat();
+
+ array.setPoint(i, static_cast<int>(x), static_cast<int>(y));
+ i++;
+ }
+
+ return array;
+ }
+
+ void parseTransform(const TQString &transform)
+ {
+ // Combine new and old matrix
+ TQWMatrix matrix = m_engine->painter()->parseTransform(transform);
+
+ TQWMatrix *current = m_engine->painter()->tqworldMatrix();
+#ifdef USE_QT4
+printf("[FIXME] *current = matrix * *current locks up under Qt4; bypassing for now\n\r");
+#else // USE_QT4
+ *current = matrix * *current;
+#endif // USE_QT4
+ }
+
+ void parseCommonAttributes(TQDomNode &node)
+ {
+ // Set important default attributes
+ m_engine->painter()->setFillColor("black");
+ m_engine->painter()->setStrokeColor("none");
+ m_engine->painter()->setStrokeDashArray("");
+ m_engine->painter()->setStrokeWidth(1);
+ m_engine->painter()->setJoinStyle("");
+ m_engine->painter()->setCapStyle("");
+ // m_engine->painter()->setFillOpacity(255, true);
+ // m_engine->painter()->setStrokeOpacity(255, true);
+
+ // Collect parent node's attributes
+ TQPtrList<TQDomNamedNodeMap> applyList;
+ applyList.setAutoDelete(true);
+
+ TQDomNode tqshape = node.parentNode();
+ for(; !tqshape.isNull() ; tqshape = tqshape.parentNode())
+ applyList.prepend(new TQDomNamedNodeMap(tqshape.attributes()));
+
+ // Apply parent attributes
+ for(TQDomNamedNodeMap *map = applyList.first(); map != 0; map = applyList.next())
+ {
+ TQDomNamedNodeMap attr = *map;
+
+ for(unsigned int i = 0; i < attr.count(); i++)
+ {
+ TQString name, value;
+
+ name = attr.item(i).nodeName().lower();
+ value = attr.item(i).nodeValue();
+
+ if(name == "transform")
+ parseTransform(value);
+ else if(name == "style")
+ parseStyle(value);
+ else
+ parsePA(name, value);
+ }
+ }
+
+ // Apply local attributes
+ TQDomNamedNodeMap attr = node.attributes();
+
+ for(unsigned int i = 0; i < attr.count(); i++)
+ {
+ TQDomNode current = attr.item(i);
+
+ if(current.nodeName().lower() == "transform")
+ parseTransform(current.nodeValue());
+ else if(current.nodeName().lower() == "style")
+ parseStyle(current.nodeValue());
+ else
+ parsePA(current.nodeName().lower(), current.nodeValue());
+ }
+ }
+
+ bool handleTags(TQDomElement element, bool paint)
+ {
+ if(element.attribute("display") == "none")
+ return false;
+ if(element.tagName() == "linearGradient")
+ {
+ ArtGradientLinear *gradient = new ArtGradientLinear();
+
+ int offsets = -1;
+ gradient->stops = parseGradientStops(element, offsets);
+ gradient->n_stops = offsets + 1;
+
+ TQString spread = element.attribute("spreadMethod");
+ if(spread == "repeat")
+ gradient->spread = ART_GRADIENT_REPEAT;
+ else if(spread == "reflect")
+ gradient->spread = ART_GRADIENT_REFLECT;
+ else
+ gradient->spread = ART_GRADIENT_PAD;
+
+ m_engine->painter()->addLinearGradient(element.attribute("id"), gradient);
+ m_engine->painter()->addLinearGradientElement(gradient, element);
+ return true;
+ }
+ else if(element.tagName() == "radialGradient")
+ {
+ ArtGradientRadial *gradient = new ArtGradientRadial();
+
+ int offsets = -1;
+ gradient->stops = parseGradientStops(element, offsets);
+ gradient->n_stops = offsets + 1;
+
+ m_engine->painter()->addRadialGradient(element.attribute("id"), gradient);
+ m_engine->painter()->addRadialGradientElement(gradient, element);
+ return true;
+ }
+
+ if(!paint)
+ return true;
+
+ // TODO: Default attribute values
+ if(element.tagName() == "rect")
+ {
+ double x = toPixel(element.attribute("x"), true);
+ double y = toPixel(element.attribute("y"), false);
+ double w = toPixel(element.attribute("width"), true);
+ double h = toPixel(element.attribute("height"), false);
+
+ double rx = 0.0;
+ double ry = 0.0;
+
+ if(element.hasAttribute("rx"))
+ rx = toPixel(element.attribute("rx"), true);
+
+ if(element.hasAttribute("ry"))
+ ry = toPixel(element.attribute("ry"), false);
+
+ m_engine->painter()->drawRectangle(x, y, w, h, rx, ry);
+ }
+ else if(element.tagName() == "switch")
+ {
+ TQDomNode iterate = element.firstChild();
+
+ while(!iterate.isNull())
+ {
+ // Reset matrix
+ m_engine->painter()->setWorldMatrix(new TQWMatrix(m_initialMatrix));
+
+ // Parse common attributes, style / transform
+ parseCommonAttributes(iterate);
+
+ if(handleTags(iterate.toElement(), true))
+ return true;
+ iterate = iterate.nextSibling();
+ }
+ return true;
+ }
+ else if(element.tagName() == "g" || element.tagName() == "defs")
+ {
+ TQDomNode iterate = element.firstChild();
+
+ while(!iterate.isNull())
+ {
+ // Reset matrix
+ m_engine->painter()->setWorldMatrix(new TQWMatrix(m_initialMatrix));
+
+ // Parse common attributes, style / transform
+ parseCommonAttributes(iterate);
+
+ handleTags(iterate.toElement(), (element.tagName() == "defs") ? false : true);
+ iterate = iterate.nextSibling();
+ }
+ return true;
+ }
+ else if(element.tagName() == "line")
+ {
+ double x1 = toPixel(element.attribute("x1"), true);
+ double y1 = toPixel(element.attribute("y1"), false);
+ double x2 = toPixel(element.attribute("x2"), true);
+ double y2 = toPixel(element.attribute("y2"), false);
+
+ m_engine->painter()->drawLine(x1, y1, x2, y2);
+ return true;
+ }
+ else if(element.tagName() == "circle")
+ {
+ double cx = toPixel(element.attribute("cx"), true);
+ double cy = toPixel(element.attribute("cy"), false);
+
+ double r = toPixel(element.attribute("r"), true); // TODO: horiz correct?
+
+ m_engine->painter()->drawEllipse(cx, cy, r, r);
+ return true;
+ }
+ else if(element.tagName() == "ellipse")
+ {
+ double cx = toPixel(element.attribute("cx"), true);
+ double cy = toPixel(element.attribute("cy"), false);
+
+ double rx = toPixel(element.attribute("rx"), true);
+ double ry = toPixel(element.attribute("ry"), false);
+
+ m_engine->painter()->drawEllipse(cx, cy, rx, ry);
+ return true;
+ }
+ else if(element.tagName() == "polyline")
+ {
+ TQPointArray polyline = parsePoints(element.attribute("points"));
+ m_engine->painter()->drawPolyline(polyline);
+ return true;
+ }
+ else if(element.tagName() == "polygon")
+ {
+ TQPointArray polygon = parsePoints(element.attribute("points"));
+ m_engine->painter()->drawPolygon(polygon);
+ return true;
+ }
+ else if(element.tagName() == "path")
+ {
+ bool filled = true;
+
+ if(element.hasAttribute("fill") && element.attribute("fill").contains("none"))
+ filled = false;
+
+ if(element.attribute("style").contains("fill") && element.attribute("style").stripWhiteSpace().contains("fill:none"))
+ filled = false;
+
+ m_engine->painter()->drawPath(element.attribute("d"), filled);
+ return true;
+ }
+ else if(element.tagName() == "image")
+ {
+ double x = toPixel(element.attribute("x"), true);
+ double y = toPixel(element.attribute("y"), false);
+ double w = toPixel(element.attribute("width"), true);
+ double h = toPixel(element.attribute("height"), false);
+
+ TQString href = element.attribute("xlink:href");
+
+ TQImage image;
+ if(href.startsWith("data:"))
+ {
+ // Get input
+ TQCString input = TQString(href.remove(TQRegExp("^data:image/.*;base64,"))).utf8();
+
+ // Decode into 'output'
+ TQByteArray output;
+ KCodecs::base64Decode(input, output);
+
+ // Display
+ image.loadFromData(output);
+ }
+ else
+ image.load(href);
+
+ if (!image.isNull())
+ {
+ // Scale, if needed
+ if(image.width() != (int) w || image.height() != (int) h)
+ image = image.smoothScale((int) w, (int) h, TQ_ScaleFree);
+
+ m_engine->painter()->drawImage(x, y, image);
+ }
+
+ return true;
+ }
+ return false;
+ }
+
+ void parseStyle(const TQString &style)
+ {
+ TQStringList substyles = TQStringList::split(';', style);
+ for(TQStringList::Iterator it = substyles.begin(); it != substyles.end(); ++it)
+ {
+ TQStringList substyle = TQStringList::split(':', (*it));
+ TQString command = substyle[0];
+ TQString params = substyle[1];
+ command = command.stripWhiteSpace();
+ params = params.stripWhiteSpace();
+
+ parsePA(command, params);
+ }
+ }
+
+ void parsePA(const TQString &command, const TQString &value)
+ {
+ if(command == "stroke-width") // TODO: horiz:false correct?
+ m_engine->painter()->setStrokeWidth(toPixel(value, false));
+ else if(command == "stroke-miterlimit")
+ m_engine->painter()->setStrokeMiterLimit(value);
+ else if(command == "stroke-linecap")
+ m_engine->painter()->setCapStyle(value);
+ else if(command == "stroke-linejoin")
+ m_engine->painter()->setJoinStyle(value);
+ else if(command == "stroke-dashoffset")
+ m_engine->painter()->setStrokeDashOffset(value);
+ else if(command == "stroke-dasharray" && value != "none")
+ m_engine->painter()->setStrokeDashArray(value);
+ else if(command == "stroke")
+ m_engine->painter()->setStrokeColor(value);
+ else if(command == "fill")
+ m_engine->painter()->setFillColor(value);
+ else if(command == "fill-rule")
+ m_engine->painter()->setFillRule(value);
+ else if(command == "fill-opacity" || command == "stroke-opacity" || command == "opacity")
+ {
+ if(command == "fill-opacity")
+ m_engine->painter()->setFillOpacity(value);
+ else if(command == "stroke-value")
+ m_engine->painter()->setStrokeOpacity(value);
+ else
+ {
+ m_engine->painter()->setOpacity(value);
+ m_engine->painter()->setFillOpacity(value);
+ m_engine->painter()->setStrokeOpacity(value);
+ }
+ }
+ }
+
+private:
+ friend class KSVGIconEngine;
+
+ KSVGIconEngine *m_engine;
+ TQWMatrix m_initialMatrix;
+};
+
+struct KSVGIconEngine::Private
+{
+ KSVGIconPainter *painter;
+ KSVGIconEngineHelper *helper;
+
+ double width;
+ double height;
+};
+
+KSVGIconEngine::KSVGIconEngine() : d(new Private())
+{
+ d->painter = 0;
+ d->helper = new KSVGIconEngineHelper(this);
+
+ d->width = 0.0;
+ d->height = 0.0;
+}
+
+KSVGIconEngine::~KSVGIconEngine()
+{
+ if(d->painter)
+ delete d->painter;
+
+ delete d->helper;
+
+ delete d;
+}
+
+bool KSVGIconEngine::load(int width, int height, const TQString &path)
+{
+ if(path.isNull()) return false;
+
+ TQDomDocument svgDocument("svg");
+ TQFile file(path);
+
+ if(path.right(3).upper() == "SVG")
+ {
+ // Open SVG Icon
+ if(!file.open(IO_ReadOnly))
+ return false;
+
+ svgDocument.setContent(&file);
+ }
+ else // SVGZ
+ {
+ gzFile svgz = gzopen(path.latin1(), "ro");
+ if(!svgz)
+ return false;
+
+ TQString data;
+ bool done = false;
+
+ TQCString buffer(1024);
+ int length = 0;
+
+ while(!done)
+ {
+ int ret = gzread(svgz, buffer.data() + length, 1024);
+ if(ret == 0)
+ done = true;
+ else if(ret == -1)
+ return false;
+ else {
+ buffer.resize(buffer.size()+1024);
+ length += ret;
+ }
+ }
+
+ gzclose(svgz);
+
+ svgDocument.setContent(buffer);
+ }
+
+ if(svgDocument.isNull())
+ return false;
+
+ // Check for root element
+ TQDomNode rootNode = svgDocument.namedItem("svg");
+ if(rootNode.isNull() || !rootNode.isElement())
+ return false;
+
+ // Detect width and height
+ TQDomElement rootElement = rootNode.toElement();
+
+ // Create icon painter
+ d->painter = new KSVGIconPainter(width, height);
+
+ d->width = width; // this sets default for no width -> 100% case
+ if(rootElement.hasAttribute("width"))
+ d->width = d->helper->toPixel(rootElement.attribute("width"), true);
+
+ d->height = height; // this sets default for no height -> 100% case
+ if(rootElement.hasAttribute("height"))
+ d->height = d->helper->toPixel(rootElement.attribute("height"), false);
+
+ // Create icon painter
+ d->painter->setDrawWidth(static_cast<int>(d->width));
+ d->painter->setDrawHeight(static_cast<int>(d->height));
+
+ // Set viewport clipping rect
+ d->painter->setClippingRect(0, 0, width, height);
+
+ // Apply viewbox
+ if(rootElement.hasAttribute("viewBox"))
+ {
+ TQStringList points = TQStringList::split(' ', rootElement.attribute("viewBox").simplifyWhiteSpace());
+
+ float w = points[2].toFloat();
+ float h = points[3].toFloat();
+
+ double vratiow = width / w;
+ double vratioh = height / h;
+
+ d->width = w;
+ d->height = h;
+
+ d->painter->tqworldMatrix()->scale(vratiow, vratioh);
+ }
+ else
+ {
+ // Fit into 'width' and 'height'
+ // FIXME: Use an aspect ratio
+ double ratiow = width / d->width;
+ double ratioh = height / d->height;
+
+ d->painter->tqworldMatrix()->scale(ratiow, ratioh);
+ }
+
+ TQWMatrix initialMatrix = *d->painter->tqworldMatrix();
+ d->helper->m_initialMatrix = initialMatrix;
+
+ // Apply transform
+ if(rootElement.hasAttribute("transform"))
+ d->helper->parseTransform(rootElement.attribute("transform"));
+
+ // Go through all elements
+ TQDomNode svgNode = rootElement.firstChild();
+ while(!svgNode.isNull())
+ {
+ TQDomElement svgChild = svgNode.toElement();
+ if(!svgChild.isNull())
+ {
+ d->helper->parseCommonAttributes(svgNode);
+ d->helper->handleTags(svgChild, true);
+ }
+
+ svgNode = svgNode.nextSibling();
+
+ // Reset matrix
+ d->painter->setWorldMatrix(new TQWMatrix(initialMatrix));
+ }
+
+ d->painter->finish();
+
+ return true;
+}
+
+KSVGIconPainter *KSVGIconEngine::painter()
+{
+ return d->painter;
+}
+
+TQImage *KSVGIconEngine::image()
+{
+ return d->painter->image();
+}
+
+double KSVGIconEngine::width()
+{
+ return d->width;
+}
+
+double KSVGIconEngine::height()
+{
+ return d->height;
+}
+
+// vim:ts=4:noet
diff --git a/tdecore/svgicons/ksvgiconengine.h b/tdecore/svgicons/ksvgiconengine.h
new file mode 100644
index 000000000..3ef2063e9
--- /dev/null
+++ b/tdecore/svgicons/ksvgiconengine.h
@@ -0,0 +1,49 @@
+/*
+ Copyright (C) 2002 Nikolas Zimmermann <wildfox@kde.org>
+ This file is part of the KDE project
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KSVGIconEngine_H
+#define KSVGIconEngine_H
+
+#include <kdelibs_export.h>
+
+/** \internal DO NOT USE! */
+
+class KSVGIconPainter;
+
+class KDECORE_EXPORT KSVGIconEngine
+{
+public:
+ KSVGIconEngine();
+ ~KSVGIconEngine();
+
+ bool load(int width, int height, const TQString &path);
+
+ KSVGIconPainter *painter();
+ TQImage *image();
+
+ double width();
+ double height();
+
+private:
+ struct Private;
+ Private *d;
+};
+
+#endif
diff --git a/tdecore/svgicons/ksvgiconpainter.cpp b/tdecore/svgicons/ksvgiconpainter.cpp
new file mode 100644
index 000000000..2f988ce78
--- /dev/null
+++ b/tdecore/svgicons/ksvgiconpainter.cpp
@@ -0,0 +1,2837 @@
+/*
+ Copyright (C) 2002 Nikolas Zimmermann <wildfox@kde.org>
+ This file is part of the KDE project
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ aint with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include <tqvaluevector.h>
+#include <tqstringlist.h>
+#include <tqwmatrix.h>
+#include <tqregexp.h>
+#include <tqimage.h>
+#include <tqdict.h>
+#include <tqmap.h>
+#include <tqdom.h>
+
+#include <math.h>
+
+#include <kdebug.h>
+
+#include <libart_lgpl/art_rgba.h>
+#include <libart_lgpl/art_bpath.h>
+#include <libart_lgpl/art_vpath.h>
+#include <libart_lgpl/art_vpath_dash.h>
+#include <libart_lgpl/art_affine.h>
+#include <libart_lgpl/art_render_svp.h>
+#include <libart_lgpl/art_svp.h>
+#include <libart_lgpl/art_svp_vpath.h>
+#include <libart_lgpl/art_svp_intersect.h>
+#include <libart_lgpl/art_svp_vpath_stroke.h>
+
+#include "ksvgiconpainter.h"
+
+#define ART_END2 10
+
+const double deg2rad = 0.017453292519943295769; // pi/180
+
+class KSVGIconPainterHelper
+{
+public:
+ KSVGIconPainterHelper(int width, int height, KSVGIconPainter *painter)
+ {
+ m_painter = painter;
+
+ m_clipSVP = 0;
+
+ m_fillColor = Qt::black;
+
+ m_useFill = true;
+ m_useStroke = false;
+
+ m_useFillGradient = false;
+ m_useStrokeGradient = false;
+
+ m_tqworldMatrix = new TQWMatrix();
+
+ // Create new image with alpha support
+ m_image = new TQImage(width, height, 32);
+ m_image->setAlphaBuffer(true);
+
+ m_strokeWidth = 1.0;
+ m_strokeMiterLimit = 4;
+ m_dashOffset = 0;
+ m_dashes = "";
+
+ m_opacity = 0xff;
+ m_fillOpacity = 0xff;
+ m_strokeOpacity = 0xff;
+
+ m_fillRule = "nonzero";
+
+ m_width = width;
+ m_height = height;
+
+ m_rowstride = m_width * 4;
+
+ // Make internal libart rendering buffer transparent
+ m_buffer = art_new(art_u8, m_rowstride * m_height);
+ memset(m_buffer, 0, m_rowstride * m_height);
+
+ m_tempBuffer = 0;
+ }
+
+ ~KSVGIconPainterHelper()
+ {
+ if(m_clipSVP)
+ art_svp_free(m_clipSVP);
+
+ art_free(m_buffer);
+
+ delete m_image;
+ delete m_tqworldMatrix;
+
+ for(TQMap<TQString, ArtGradientLinear *>::Iterator it = m_linearGradientMap.begin(); it != m_linearGradientMap.end(); ++it)
+ {
+ if (!it.data())
+ continue;
+ delete [] it.data()->stops;
+ delete it.data();
+ }
+ for(TQMap<TQString, ArtGradientRadial *>::Iterator it = m_radialGradientMap.begin(); it != m_radialGradientMap.end(); ++it)
+ {
+ if (!it.data())
+ continue;
+ delete [] it.data()->stops;
+ delete it.data();
+ }
+ }
+
+ ArtVpath *allocVPath(int number)
+ {
+ return art_new(ArtVpath, number);
+ }
+
+ ArtBpath *allocBPath(int number)
+ {
+ return art_new(ArtBpath, number);
+ }
+
+ void ensureSpace(TQMemArray<ArtBpath> &vec, int index)
+ {
+ if(vec.size() == (unsigned int) index)
+ vec.resize(index + 1);
+ }
+
+ void createBuffer()
+ {
+ m_tempBuffer = art_new(art_u8, m_rowstride * m_height);
+ memset(m_tempBuffer, 0, m_rowstride * m_height);
+
+ // Swap buffers, so we work with the new one internally...
+ art_u8 *temp = m_buffer;
+ m_buffer = m_tempBuffer;
+ m_tempBuffer = temp;
+ }
+
+ void mixBuffer(int opacity)
+ {
+ art_u8 *srcPixel = m_buffer;
+ art_u8 *dstPixel = m_tempBuffer;
+
+ for(int y = 0; y < m_height; y++)
+ {
+ for(int x = 0; x < m_width; x++)
+ {
+ art_u8 r, g, b, a;
+
+ a = srcPixel[4 * x + 3];
+
+ if(a)
+ {
+ r = srcPixel[4 * x];
+ g = srcPixel[4 * x + 1];
+ b = srcPixel[4 * x + 2];
+
+ int temp = a * opacity + 0x80;
+ a = (temp + (temp >> 8)) >> 8;
+ art_rgba_run_alpha(dstPixel + 4 * x, r, g, b, a, 1);
+ }
+ }
+
+ srcPixel += m_rowstride;
+ dstPixel += m_rowstride;
+ }
+
+ // Re-swap again...
+ art_u8 *temp = m_buffer;
+ m_buffer = m_tempBuffer;
+ m_tempBuffer = temp;
+
+ art_free(m_tempBuffer);
+ m_tempBuffer = 0;
+ }
+
+ TQ_UINT32 toArtColor(const TQColor &color)
+ {
+ // Convert in a libart suitable form
+ TQString tempName = color.name();
+ const char *str = tempName.latin1();
+
+ int result = 0;
+
+ for(int i = 1; str[i]; i++)
+ {
+ int hexval;
+ if(str[i] >= '0' && str[i] <= '9')
+ hexval = str[i] - '0';
+ else if (str[i] >= 'A' && str[i] <= 'F')
+ hexval = str[i] - 'A' + 10;
+ else if (str[i] >= 'a' && str[i] <= 'f')
+ hexval = str[i] - 'a' + 10;
+ else
+ break;
+
+ result = (result << 4) + hexval;
+ }
+
+ return result;
+ }
+
+ void drawSVP(ArtSVP *svp, TQ_UINT32 rgb, int opacity)
+ {
+ if(!svp)
+ return;
+
+ ArtRender *render = art_render_new(0, 0, m_width, m_height, m_buffer, m_rowstride, 3, 8, ART_ALPHA_SEPARATE, 0);
+ art_render_svp(render, svp);
+
+ art_render_mask_solid(render, (opacity << 8) + opacity + (opacity >> 7));
+
+ ArtPixMaxDepth color[3];
+ color[0] = ART_PIX_MAX_FROM_8(rgb >> 16);
+ color[1] = ART_PIX_MAX_FROM_8((rgb >> 8) & 0xff);
+ color[2] = ART_PIX_MAX_FROM_8(rgb & 0xff);
+
+ art_render_image_solid(render, color);
+ art_render_invoke(render);
+ }
+
+ void drawBPath(ArtBpath *bpath)
+ {
+ double affine[6];
+ affine[0] = m_tqworldMatrix->m11();
+ affine[1] = m_tqworldMatrix->m12();
+ affine[2] = m_tqworldMatrix->m21();
+ affine[3] = m_tqworldMatrix->m22();
+ affine[4] = m_tqworldMatrix->dx();
+ affine[5] = m_tqworldMatrix->dy();
+
+ ArtBpath *temp = art_bpath_affine_transform(bpath, affine);
+ ArtVpath *vec = art_bez_path_to_vec(temp, 0.25);
+ art_free(temp);
+ drawPathInternal(vec, affine);
+ }
+
+ void drawVPath(ArtVpath *vec)
+ {
+ double affine[6];
+ affine[0] = m_tqworldMatrix->m11();
+ affine[1] = m_tqworldMatrix->m12();
+ affine[2] = m_tqworldMatrix->m21();
+ affine[3] = m_tqworldMatrix->m22();
+ affine[4] = m_tqworldMatrix->dx();
+ affine[5] = m_tqworldMatrix->dy();
+
+ ArtVpath *temp = art_vpath_affine_transform(vec, affine);
+ art_free(vec);
+ vec = temp;
+ drawPathInternal(vec, affine);
+ }
+
+ void drawPathInternal(ArtVpath *vec, double *affine)
+ {
+ ArtSVP *svp;
+ ArtSVP *fillSVP = 0, *strokeSVP = 0;
+
+ TQ_UINT32 fillColor = 0, strokeColor = 0;
+
+ // Filling
+ {
+ int index = -1;
+ TQValueVector<int> toCorrect;
+ while(vec[++index].code != ART_END)
+ {
+ if(vec[index].code == ART_END2)
+ {
+ vec[index].code = ART_LINETO;
+ toCorrect.push_back(index);
+ }
+ }
+
+ fillColor = toArtColor(m_fillColor);
+
+ ArtSvpWriter *swr;
+ ArtSVP *temp;
+ temp = art_svp_from_vpath(vec);
+
+ if(m_fillRule == "evenodd")
+ swr = art_svp_writer_rewind_new(ART_WIND_RULE_ODDEVEN);
+ else
+ swr = art_svp_writer_rewind_new(ART_WIND_RULE_NONZERO);
+
+ art_svp_intersector(temp, swr);
+ svp = art_svp_writer_rewind_reap(swr);
+
+ fillSVP = svp;
+
+ art_svp_free(temp);
+
+ TQValueVector<int>::iterator it;
+ for(it = toCorrect.begin(); it != toCorrect.end(); ++it)
+ vec[(*it)].code = (ArtPathcode)ART_END2;
+ }
+
+ // There seems to be a problem when stroke width is zero, this is a quick
+ // fix (Rob).
+ if(m_strokeWidth <= 0)
+ m_useStroke = m_useStrokeGradient = false;
+
+ // Stroking
+ if(m_useStroke || m_useStrokeGradient)
+ {
+ strokeColor = toArtColor(m_strokeColor);
+
+ double ratio = art_affine_expansion(affine);
+ double strokeWidth = m_strokeWidth * ratio;
+
+ ArtPathStrokeJoinType joinStyle = ART_PATH_STROKE_JOIN_MITER;
+ ArtPathStrokeCapType capStyle = ART_PATH_STROKE_CAP_BUTT;
+
+ if(m_joinStyle == "miter")
+ joinStyle = ART_PATH_STROKE_JOIN_MITER;
+ else if(m_joinStyle == "round")
+ joinStyle = ART_PATH_STROKE_JOIN_ROUND;
+ else if(m_joinStyle == "bevel")
+ joinStyle = ART_PATH_STROKE_JOIN_BEVEL;
+
+ if(m_capStyle == "butt")
+ capStyle = ART_PATH_STROKE_CAP_BUTT;
+ else if(m_capStyle == "round")
+ capStyle = ART_PATH_STROKE_CAP_ROUND;
+ else if(m_capStyle == "square")
+ capStyle = ART_PATH_STROKE_CAP_SQUARE;
+
+ if(m_dashes.length() > 0)
+ {
+ TQRegExp reg("[, ]");
+ TQStringList dashList = TQStringList::split(reg, m_dashes);
+
+ double *dashes = new double[dashList.count()];
+ for(unsigned int i = 0; i < dashList.count(); i++)
+ dashes[i] = m_painter->toPixel(dashList[i], true);
+
+ ArtVpathDash dash;
+ dash.offset = m_dashOffset;
+ dash.n_dash = dashList.count();
+
+ dash.dash = dashes;
+
+ ArtVpath *vec2 = art_vpath_dash(vec, &dash);
+ art_free(vec);
+
+ delete[] dashes;
+
+ vec = vec2;
+ }
+
+ svp = art_svp_vpath_stroke(vec, joinStyle, capStyle, strokeWidth, m_strokeMiterLimit, 0.25);
+
+ strokeSVP = svp;
+ }
+
+ // Apply opacity
+ int fillOpacity = static_cast<int>(m_fillOpacity);
+ int strokeOpacity = static_cast<int>(m_strokeOpacity);
+ int opacity = static_cast<int>(m_opacity);
+
+ // Needed hack, to support both transparent
+ // paths and transparent gradients
+ if(fillOpacity == strokeOpacity && fillOpacity == opacity && !m_useFillGradient && !m_useStrokeGradient)
+ opacity = 255;
+
+ if(fillOpacity != 255)
+ {
+ int temp = fillOpacity * opacity + 0x80;
+ fillOpacity = (temp + (temp >> 8)) >> 8;
+ }
+
+ if(strokeOpacity != 255)
+ {
+ int temp = strokeOpacity * opacity + 0x80;
+ strokeOpacity = (temp + (temp >> 8)) >> 8;
+ }
+
+ // Create temporary buffer if necessary
+ bool tempDone = false;
+ if(m_opacity != 0xff)
+ {
+ tempDone = true;
+ createBuffer();
+ }
+
+ // Apply Gradients on fill/stroke
+ if(m_useFillGradient)
+ applyGradient(fillSVP, true);
+ else if(m_useFill)
+ drawSVP(fillSVP, fillColor, fillOpacity);
+
+ if(m_useStrokeGradient)
+ applyGradient(strokeSVP, false);
+ else if(m_useStroke)
+ drawSVP(strokeSVP, strokeColor, strokeOpacity);
+
+ // Mix in temporary buffer, if possible
+ if(tempDone)
+ mixBuffer(opacity);
+
+ if(m_clipSVP)
+ {
+ art_svp_free(m_clipSVP);
+ m_clipSVP = 0;
+ }
+
+ if(fillSVP)
+ art_svp_free(fillSVP);
+
+ if(strokeSVP)
+ art_svp_free(strokeSVP);
+
+ // Reset opacity values
+ m_opacity = 255.0;
+ m_fillOpacity = 255.0;
+ m_strokeOpacity = 255.0;
+
+ art_free(vec);
+ }
+
+ void applyLinearGradient(ArtSVP *svp, const TQString &ref)
+ {
+ ArtGradientLinear *linear = m_linearGradientMap[ref];
+ if(linear)
+ {
+ TQDomElement element = m_linearGradientElementMap[linear];
+
+ double x1, y1, x2, y2;
+ if(element.hasAttribute("x1"))
+ x1 = m_painter->toPixel(element.attribute("x1"), true);
+ else
+ x1 = 0;
+
+ if(element.hasAttribute("y1"))
+ y1 = m_painter->toPixel(element.attribute("y1"), false);
+ else
+ y1 = 0;
+
+ if(element.hasAttribute("x2"))
+ x2 = m_painter->toPixel(element.attribute("x2"), true);
+ else
+ x2 = 100;
+
+ if(element.hasAttribute("y2"))
+ y2 = m_painter->toPixel(element.attribute("y2"), false);
+ else
+ y2 = 0;
+
+ // Adjust to gradientTransform
+ TQWMatrix m = m_painter->parseTransform(element.attribute("gradientTransform"));
+ m.map(x1, y1, &x1, &y1);
+ m.map(x2, y2, &x2, &y2);
+
+ double x1n = x1 * m_tqworldMatrix->m11() + y1 * m_tqworldMatrix->m21() + m_tqworldMatrix->dx();
+ double y1n = x1 * m_tqworldMatrix->m12() + y1 * m_tqworldMatrix->m22() + m_tqworldMatrix->dy();
+ double x2n = x2 * m_tqworldMatrix->m11() + y2 * m_tqworldMatrix->m21() + m_tqworldMatrix->dx();
+ double y2n = x2 * m_tqworldMatrix->m12() + y2 * m_tqworldMatrix->m22() + m_tqworldMatrix->dy();
+
+ double dx = x2n - x1n;
+ double dy = y2n - y1n;
+ double scale = 1.0 / (dx * dx + dy * dy);
+
+ linear->a = dx * scale;
+ linear->b = dy * scale;
+ linear->c = -(x1n * linear->a + y1n * linear->b);
+
+ ArtRender *render = art_render_new(0, 0, m_width, m_height, m_buffer, m_rowstride, 3, 8, ART_ALPHA_SEPARATE, 0);
+ art_render_svp(render, svp);
+
+ art_render_gradient_linear(render, linear, ART_FILTER_HYPER);
+ art_render_invoke(render);
+ }
+ }
+
+ void applyRadialGradient(ArtSVP *svp, const TQString &ref)
+ {
+ ArtGradientRadial *radial = m_radialGradientMap[ref];
+ if(radial)
+ {
+ TQDomElement element = m_radialGradientElementMap[radial];
+
+ double cx, cy, r, fx, fy;
+ if(element.hasAttribute("cx"))
+ cx = m_painter->toPixel(element.attribute("cx"), true);
+ else
+ cx = 50;
+
+ if(element.hasAttribute("cy"))
+ cy = m_painter->toPixel(element.attribute("cy"), false);
+ else
+ cy = 50;
+
+ if(element.hasAttribute("r"))
+ r = m_painter->toPixel(element.attribute("r"), true);
+ else
+ r = 50;
+
+ if(element.hasAttribute("fx"))
+ fx = m_painter->toPixel(element.attribute("fx"), false);
+ else
+ fx = cx;
+
+ if(element.hasAttribute("fy"))
+ fy = m_painter->toPixel(element.attribute("fy"), false);
+ else
+ fy = cy;
+
+ radial->affine[0] = m_tqworldMatrix->m11();
+ radial->affine[1] = m_tqworldMatrix->m12();
+ radial->affine[2] = m_tqworldMatrix->m21();
+ radial->affine[3] = m_tqworldMatrix->m22();
+ radial->affine[4] = m_tqworldMatrix->dx();
+ radial->affine[5] = m_tqworldMatrix->dy();
+
+ radial->fx = (fx - cx) / r;
+ radial->fy = (fy - cy) / r;
+
+ double aff1[6], aff2[6], gradTransform[6];
+
+ // Respect gradientTransform
+ TQWMatrix m = m_painter->parseTransform(element.attribute("gradientTransform"));
+
+ gradTransform[0] = m.m11();
+ gradTransform[1] = m.m12();
+ gradTransform[2] = m.m21();
+ gradTransform[3] = m.m22();
+ gradTransform[4] = m.dx();
+ gradTransform[5] = m.dy();
+
+ art_affine_scale(aff1, r, r);
+ art_affine_translate(aff2, cx, cy);
+
+ art_affine_multiply(aff1, aff1, aff2);
+ art_affine_multiply(aff1, aff1, gradTransform);
+ art_affine_multiply(aff1, aff1, radial->affine);
+ art_affine_invert(radial->affine, aff1);
+
+ ArtRender *render = art_render_new(0, 0, m_width, m_height, m_buffer, m_rowstride, 3, 8, ART_ALPHA_SEPARATE, 0);
+ art_render_svp(render, svp);
+
+ art_render_gradient_radial(render, radial, ART_FILTER_HYPER);
+ art_render_invoke(render);
+ }
+ }
+
+ void applyGradient(ArtSVP *svp, const TQString &ref)
+ {
+ ArtGradientLinear *linear = m_linearGradientMap[ref];
+ if(linear)
+ {
+ TQDomElement element = m_linearGradientElementMap[linear];
+
+ if(!element.hasAttribute("xlink:href"))
+ {
+ applyLinearGradient(svp, ref);
+ return;
+ }
+ else
+ {
+ ArtGradientLinear *linear = m_linearGradientMap[element.attribute("xlink:href").mid(1)];
+ TQDomElement newElement = m_linearGradientElementMap[linear];
+
+ // Saved 'old' attributes
+ TQDict<TQString> refattrs;
+ refattrs.setAutoDelete(true);
+
+ for(unsigned int i = 0; i < newElement.attributes().length(); ++i)
+ refattrs.insert(newElement.attributes().item(i).nodeName(), new TQString(newElement.attributes().item(i).nodeValue()));
+
+ // Copy attributes
+ if(!newElement.isNull())
+ {
+ TQDomNamedNodeMap attr = element.attributes();
+
+ for(unsigned int i = 0; i < attr.length(); i++)
+ {
+ TQString name = attr.item(i).nodeName();
+ if(name != "xlink:href" && name != "id")
+ newElement.setAttribute(name, attr.item(i).nodeValue());
+ }
+ }
+
+ applyGradient(svp, element.attribute("xlink:href").mid(1));
+
+ // Restore attributes
+ TQDictIterator<TQString> itr(refattrs);
+ for(; itr.current(); ++itr)
+ newElement.setAttribute(itr.currentKey(), *(itr.current()));
+
+ return;
+ }
+ }
+
+ ArtGradientRadial *radial = m_radialGradientMap[ref];
+ if(radial)
+ {
+ TQDomElement element = m_radialGradientElementMap[radial];
+
+ if(!element.hasAttribute("xlink:href"))
+ {
+ applyRadialGradient(svp, ref);
+ return;
+ }
+ else
+ {
+ ArtGradientRadial *radial = m_radialGradientMap[element.attribute("xlink:href").mid(1)];
+ TQDomElement newElement = m_radialGradientElementMap[radial];
+
+ // Saved 'old' attributes
+ TQDict<TQString> refattrs;
+ refattrs.setAutoDelete(true);
+
+ for(unsigned int i = 0; i < newElement.attributes().length(); ++i)
+ refattrs.insert(newElement.attributes().item(i).nodeName(), new TQString(newElement.attributes().item(i).nodeValue()));
+
+ // Copy attributes
+ if(!newElement.isNull())
+ {
+ TQDomNamedNodeMap attr = element.attributes();
+
+ for(unsigned int i = 0; i < attr.length(); i++)
+ {
+ TQString name = attr.item(i).nodeName();
+ if(name != "xlink:href" && name != "id")
+ newElement.setAttribute(name, attr.item(i).nodeValue());
+ }
+ }
+
+ applyGradient(svp, element.attribute("xlink:href").mid(1));
+
+ // Restore attributes
+ TQDictIterator<TQString> itr(refattrs);
+ for(; itr.current(); ++itr)
+ newElement.setAttribute(itr.currentKey(), *(itr.current()));
+
+ return;
+ }
+ }
+ }
+
+ void applyGradient(ArtSVP *svp, bool fill)
+ {
+ TQString ref;
+
+ if(fill)
+ {
+ m_useFillGradient = false;
+ ref = m_fillGradientReference;
+ }
+ else
+ {
+ m_useStrokeGradient = false;
+ ref = m_strokeGradientReference;
+ }
+
+ applyGradient(svp, ref);
+ }
+
+ void blit()
+ {
+ unsigned char *line = m_buffer;
+
+ for(int y = 0; y < m_height; y++)
+ {
+ QRgb *sl = reinterpret_cast<QRgb *>(m_image->scanLine(y));
+ for(int x = 0; x < m_width; x++)
+ sl[x] = tqRgba(line[x * 4], line[x * 4 + 1], line[x * 4 + 2], line[x * 4 + 3]);
+
+ line += m_rowstride;
+ }
+ }
+
+ void calculateArc(bool relative, TQMemArray<ArtBpath> &vec, int &index, double &curx, double &cury, double angle, double x, double y, double r1, double r2, bool largeArcFlag, bool sweepFlag)
+ {
+ double sin_th, cos_th;
+ double a00, a01, a10, a11;
+ double x0, y0, x1, y1, xc, yc;
+ double d, sfactor, sfactor_sq;
+ double th0, th1, th_arc;
+ int i, n_segs;
+
+ sin_th = sin(angle * (M_PI / 180.0));
+ cos_th = cos(angle * (M_PI / 180.0));
+
+ double dx;
+
+ if(!relative)
+ dx = (curx - x) / 2.0;
+ else
+ dx = -x / 2.0;
+
+ double dy;
+
+ if(!relative)
+ dy = (cury - y) / 2.0;
+ else
+ dy = -y / 2.0;
+
+ double _x1 = cos_th * dx + sin_th * dy;
+ double _y1 = -sin_th * dx + cos_th * dy;
+ double Pr1 = r1 * r1;
+ double Pr2 = r2 * r2;
+ double Px = _x1 * _x1;
+ double Py = _y1 * _y1;
+
+ // Spec : check if radii are large enough
+ double check = Px / Pr1 + Py / Pr2;
+ if(check > 1)
+ {
+ r1 = r1 * sqrt(check);
+ r2 = r2 * sqrt(check);
+ }
+
+ a00 = cos_th / r1;
+ a01 = sin_th / r1;
+ a10 = -sin_th / r2;
+ a11 = cos_th / r2;
+
+ x0 = a00 * curx + a01 * cury;
+ y0 = a10 * curx + a11 * cury;
+
+ if(!relative)
+ x1 = a00 * x + a01 * y;
+ else
+ x1 = a00 * (curx + x) + a01 * (cury + y);
+
+ if(!relative)
+ y1 = a10 * x + a11 * y;
+ else
+ y1 = a10 * (curx + x) + a11 * (cury + y);
+
+ /* (x0, y0) is current point in transformed coordinate space.
+ (x1, y1) is new point in transformed coordinate space.
+
+ The arc fits a unit-radius circle in this space.
+ */
+
+ d = (x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0);
+
+ sfactor_sq = 1.0 / d - 0.25;
+
+ if(sfactor_sq < 0)
+ sfactor_sq = 0;
+
+ sfactor = sqrt(sfactor_sq);
+
+ if(sweepFlag == largeArcFlag)
+ sfactor = -sfactor;
+
+ xc = 0.5 * (x0 + x1) - sfactor * (y1 - y0);
+ yc = 0.5 * (y0 + y1) + sfactor * (x1 - x0);
+
+ /* (xc, yc) is center of the circle. */
+ th0 = atan2(y0 - yc, x0 - xc);
+ th1 = atan2(y1 - yc, x1 - xc);
+
+ th_arc = th1 - th0;
+ if(th_arc < 0 && sweepFlag)
+ th_arc += 2 * M_PI;
+ else if(th_arc > 0 && !sweepFlag)
+ th_arc -= 2 * M_PI;
+
+ n_segs = (int) (int) ceil(fabs(th_arc / (M_PI * 0.5 + 0.001)));
+
+ for(i = 0; i < n_segs; i++)
+ {
+ index++;
+
+ ensureSpace(vec, index);
+
+ {
+ double sin_th, cos_th;
+ double a00, a01, a10, a11;
+ double x1, y1, x2, y2, x3, y3;
+ double t;
+ double th_half;
+
+ double _th0 = th0 + i * th_arc / n_segs;
+ double _th1 = th0 + (i + 1) * th_arc / n_segs;
+
+ sin_th = sin(angle * (M_PI / 180.0));
+ cos_th = cos(angle * (M_PI / 180.0));
+
+ /* inverse transform compared with rsvg_path_arc */
+ a00 = cos_th * r1;
+ a01 = -sin_th * r2;
+ a10 = sin_th * r1;
+ a11 = cos_th * r2;
+
+ th_half = 0.5 * (_th1 - _th0);
+ t = (8.0 / 3.0) * sin(th_half * 0.5) * sin(th_half * 0.5) / sin(th_half);
+ x1 = xc + cos(_th0) - t * sin(_th0);
+ y1 = yc + sin(_th0) + t * cos(_th0);
+ x3 = xc + cos(_th1);
+ y3 = yc + sin(_th1);
+ x2 = x3 + t * sin(_th1);
+ y2 = y3 - t * cos(_th1);
+
+ ensureSpace(vec, index);
+
+ vec[index].code = ART_CURVETO;
+ vec[index].x1 = a00 * x1 + a01 * y1;
+ vec[index].y1 = a10 * x1 + a11 * y1;
+ vec[index].x2 = a00 * x2 + a01 * y2;
+ vec[index].y2 = a10 * x2 + a11 * y2;
+ vec[index].x3 = a00 * x3 + a01 * y3;
+ vec[index].y3 = a10 * x3 + a11 * y3;
+ }
+ }
+
+ if(!relative)
+ curx = x;
+ else
+ curx += x;
+
+ if(!relative)
+ cury = y;
+ else
+ cury += y;
+ }
+
+ // For any docs, see the libart library
+ static void art_vpath_render_bez(ArtVpath **p_vpath, int *pn, int *pn_max,
+ double x0, double y0,
+ double x1, double y1,
+ double x2, double y2,
+ double x3, double y3,
+ double flatness)
+ {
+ double x3_0, y3_0, z3_0_dot, z1_dot, z2_dot;
+ double z1_perp, z2_perp, max_perp_sq;
+
+ double x_m, y_m, xa1, ya1, xa2, ya2, xb1, yb1, xb2, yb2;
+
+ x3_0 = x3 - x0;
+ y3_0 = y3 - y0;
+
+ z3_0_dot = x3_0 * x3_0 + y3_0 * y3_0;
+
+ if (z3_0_dot < 0.001)
+ goto nosubdivide;
+
+ max_perp_sq = flatness * flatness * z3_0_dot;
+
+ z1_perp = (y1 - y0) * x3_0 - (x1 - x0) * y3_0;
+ if (z1_perp * z1_perp > max_perp_sq)
+ goto subdivide;
+
+ z2_perp = (y3 - y2) * x3_0 - (x3 - x2) * y3_0;
+ if (z2_perp * z2_perp > max_perp_sq)
+ goto subdivide;
+
+ z1_dot = (x1 - x0) * x3_0 + (y1 - y0) * y3_0;
+ if (z1_dot < 0 && z1_dot * z1_dot > max_perp_sq)
+ goto subdivide;
+
+ z2_dot = (x3 - x2) * x3_0 + (y3 - y2) * y3_0;
+ if (z2_dot < 0 && z2_dot * z2_dot > max_perp_sq)
+ goto subdivide;
+
+ if (z1_dot + z1_dot > z3_0_dot)
+ goto subdivide;
+
+ if (z2_dot + z2_dot > z3_0_dot)
+ goto subdivide;
+
+ nosubdivide:
+ art_vpath_add_point (p_vpath, pn, pn_max, ART_LINETO, x3, y3);
+ return;
+
+ subdivide:
+ xa1 = (x0 + x1) * 0.5;
+ ya1 = (y0 + y1) * 0.5;
+ xa2 = (x0 + 2 * x1 + x2) * 0.25;
+ ya2 = (y0 + 2 * y1 + y2) * 0.25;
+ xb1 = (x1 + 2 * x2 + x3) * 0.25;
+ yb1 = (y1 + 2 * y2 + y3) * 0.25;
+ xb2 = (x2 + x3) * 0.5;
+ yb2 = (y2 + y3) * 0.5;
+ x_m = (xa2 + xb1) * 0.5;
+ y_m = (ya2 + yb1) * 0.5;
+ art_vpath_render_bez (p_vpath, pn, pn_max, x0, y0, xa1, ya1, xa2, ya2, x_m, y_m, flatness);
+ art_vpath_render_bez (p_vpath, pn, pn_max, x_m, y_m, xb1, yb1, xb2, yb2, x3, y3, flatness);
+ }
+
+ ArtVpath *art_bez_path_to_vec(const ArtBpath *bez, double flatness)
+ {
+ ArtVpath *vec;
+ int vec_n, vec_n_max;
+ int bez_index;
+ double x, y;
+
+ vec_n = 0;
+ vec_n_max = (1 << 4);
+ vec = art_new (ArtVpath, vec_n_max);
+
+ x = 0;
+ y = 0;
+
+ bez_index = 0;
+ do
+ {
+ if(vec_n >= vec_n_max)
+ art_expand (vec, ArtVpath, vec_n_max);
+
+ switch (bez[bez_index].code)
+ {
+ case ART_MOVETO_OPEN:
+ case ART_MOVETO:
+ case ART_LINETO:
+ x = bez[bez_index].x3;
+ y = bez[bez_index].y3;
+ vec[vec_n].code = bez[bez_index].code;
+ vec[vec_n].x = x;
+ vec[vec_n].y = y;
+ vec_n++;
+ break;
+ case ART_END:
+ vec[vec_n].code = ART_END;
+ vec[vec_n].x = 0;
+ vec[vec_n].y = 0;
+ vec_n++;
+ break;
+ case ART_END2:
+ vec[vec_n].code = (ArtPathcode)ART_END2;
+ vec[vec_n].x = bez[bez_index].x3;
+ vec[vec_n].y = bez[bez_index].y3;
+ vec_n++;
+ break;
+ case ART_CURVETO:
+ art_vpath_render_bez (&vec, &vec_n, &vec_n_max,
+ x, y,
+ bez[bez_index].x1, bez[bez_index].y1,
+ bez[bez_index].x2, bez[bez_index].y2,
+ bez[bez_index].x3, bez[bez_index].y3,
+ flatness);
+ x = bez[bez_index].x3;
+ y = bez[bez_index].y3;
+ break;
+ }
+ }
+
+ while (bez[bez_index++].code != ART_END);
+ return vec;
+ }
+
+ static void art_rgb_affine_run(int *p_x0, int *p_x1, int y,
+ int src_width, int src_height,
+ const double affine[6])
+ {
+ int x0, x1;
+ double z;
+ double x_intercept;
+ int xi;
+
+ x0 = *p_x0;
+ x1 = *p_x1;
+
+ if (affine[0] > 1e-6)
+ {
+ z = affine[2] * (y + 0.5) + affine[4];
+ x_intercept = -z / affine[0];
+ xi = (int) (int) ceil (x_intercept + 1e-6 - 0.5);
+ if (xi > x0)
+ x0 = xi;
+ x_intercept = (-z + src_width) / affine[0];
+ xi = (int) ceil (x_intercept - 1e-6 - 0.5);
+ if (xi < x1)
+ x1 = xi;
+ }
+ else if (affine[0] < -1e-6)
+ {
+ z = affine[2] * (y + 0.5) + affine[4];
+ x_intercept = (-z + src_width) / affine[0];
+ xi = (int) ceil (x_intercept + 1e-6 - 0.5);
+ if (xi > x0)
+ x0 = xi;
+ x_intercept = -z / affine[0];
+ xi = (int) ceil (x_intercept - 1e-6 - 0.5);
+ if (xi < x1)
+ x1 = xi;
+ }
+ else
+ {
+ z = affine[2] * (y + 0.5) + affine[4];
+ if (z < 0 || z >= src_width)
+ {
+ *p_x1 = *p_x0;
+ return;
+ }
+ }
+ if (affine[1] > 1e-6)
+ {
+ z = affine[3] * (y + 0.5) + affine[5];
+ x_intercept = -z / affine[1];
+ xi = (int) ceil (x_intercept + 1e-6 - 0.5);
+ if (xi > x0)
+ x0 = xi;
+ x_intercept = (-z + src_height) / affine[1];
+ xi = (int) ceil (x_intercept - 1e-6 - 0.5);
+ if (xi < x1)
+ x1 = xi;
+ }
+ else if (affine[1] < -1e-6)
+ {
+ z = affine[3] * (y + 0.5) + affine[5];
+ x_intercept = (-z + src_height) / affine[1];
+ xi = (int) ceil (x_intercept + 1e-6 - 0.5);
+ if (xi > x0)
+ x0 = xi;
+ x_intercept = -z / affine[1];
+ xi = (int) ceil (x_intercept - 1e-6 - 0.5);
+ if (xi < x1)
+ x1 = xi;
+ }
+ else
+ {
+ z = affine[3] * (y + 0.5) + affine[5];
+ if (z < 0 || z >= src_height)
+ {
+ *p_x1 = *p_x0;
+ return;
+ }
+ }
+
+ *p_x0 = x0;
+ *p_x1 = x1;
+ }
+
+ // Slightly modified version to support RGBA buffers, copied from gnome-print
+ static void art_rgba_rgba_affine(art_u8 *dst,
+ int x0, int y0, int x1, int y1, int dst_rowstride,
+ const art_u8 *src,
+ int src_width, int src_height, int src_rowstride,
+ const double affine[6])
+ {
+ int x, y;
+ double inv[6];
+ art_u8 *dst_p, *dst_linestart;
+ const art_u8 *src_p;
+ ArtPoint pt, src_pt;
+ int src_x, src_y;
+ int alpha;
+ art_u8 bg_r, bg_g, bg_b, bg_a, cr, cg, cb;
+ art_u8 fg_r, fg_g, fg_b;
+ int tmp;
+ int run_x0, run_x1;
+
+ dst_linestart = dst;
+ art_affine_invert (inv, affine);
+ for (y = y0; y < y1; y++)
+ {
+ pt.y = y + 0.5;
+ run_x0 = x0;
+ run_x1 = x1;
+ art_rgb_affine_run (&run_x0, &run_x1, y, src_width, src_height,
+ inv);
+ dst_p = dst_linestart + (run_x0 - x0) * 4;
+ for (x = run_x0; x < run_x1; x++)
+ {
+ pt.x = x + 0.5;
+ art_affine_point (&src_pt, &pt, inv);
+ src_x = (int) floor (src_pt.x);
+ src_y = (int) floor (src_pt.y);
+ src_p = src + (src_y * src_rowstride) + src_x * 4;
+ if (src_x >= 0 && src_x < src_width &&
+ src_y >= 0 && src_y < src_height)
+ {
+
+ alpha = src_p[3];
+ if (alpha)
+ {
+ if (alpha == 255)
+ {
+ dst_p[0] = src_p[0];
+ dst_p[1] = src_p[1];
+ dst_p[2] = src_p[2];
+ dst_p[3] = 255;
+ }
+ else
+ {
+ bg_r = dst_p[0];
+ bg_g = dst_p[1];
+ bg_b = dst_p[2];
+ bg_a = dst_p[3];
+
+ cr = (bg_r * bg_a + 0x80) >> 8;
+ cg = (bg_g * bg_g + 0x80) >> 8;
+ cb = (bg_b * bg_b + 0x80) >> 8;
+
+ tmp = (src_p[0] - bg_r) * alpha;
+ fg_r = bg_r + ((tmp + (tmp >> 8) + 0x80) >> 8);
+ tmp = (src_p[1] - bg_g) * alpha;
+ fg_g = bg_g + ((tmp + (tmp >> 8) + 0x80) >> 8);
+ tmp = (src_p[2] - bg_b) * alpha;
+ fg_b = bg_b + ((tmp + (tmp >> 8) + 0x80) >> 8);
+
+ dst_p[0] = fg_r;
+ dst_p[1] = fg_g;
+ dst_p[2] = fg_b;
+ dst_p[3] = bg_a + (((255 - bg_a) * alpha + 0x80) >> 8);
+ }
+ }
+ } else { dst_p[0] = 255; dst_p[1] = 0; dst_p[2] = 0; dst_p[3] = 255;}
+ dst_p += 4;
+ }
+ dst_linestart += dst_rowstride;
+ }
+ }
+
+private:
+ friend class KSVGIconPainter;
+ ArtSVP *m_clipSVP;
+
+ TQImage *m_image;
+ TQWMatrix *m_tqworldMatrix;
+
+ TQString m_fillRule;
+ TQString m_joinStyle;
+ TQString m_capStyle;
+
+ int m_strokeMiterLimit;
+
+ TQString m_dashes;
+ unsigned short m_dashOffset;
+
+ TQColor m_fillColor;
+ TQColor m_strokeColor;
+
+ art_u8 *m_buffer;
+ art_u8 *m_tempBuffer;
+
+ int m_width;
+ int m_height;
+
+ int m_rowstride;
+
+ double m_opacity;
+ double m_fillOpacity;
+ double m_strokeOpacity;
+
+ bool m_useFill;
+ bool m_useStroke;
+
+ bool m_useFillGradient;
+ bool m_useStrokeGradient;
+
+ TQString m_fillGradientReference;
+ TQString m_strokeGradientReference;
+
+ TQMap<TQString, ArtGradientLinear *> m_linearGradientMap;
+ TQMap<ArtGradientLinear *, TQDomElement> m_linearGradientElementMap;
+
+ TQMap<TQString, ArtGradientRadial *> m_radialGradientMap;
+ TQMap<ArtGradientRadial *, TQDomElement> m_radialGradientElementMap;
+
+ KSVGIconPainter *m_painter;
+
+ double m_strokeWidth;
+};
+
+struct KSVGIconPainter::Private
+{
+ KSVGIconPainterHelper *helper;
+
+ int drawWidth;
+ int drawHeight;
+};
+
+KSVGIconPainter::KSVGIconPainter(int width, int height) : d(new Private())
+{
+ d->helper = new KSVGIconPainterHelper(width, height, this);
+
+ d->drawWidth = width;
+ d->drawHeight = height;
+}
+
+KSVGIconPainter::~KSVGIconPainter()
+{
+ delete d->helper;
+ delete d;
+}
+
+void KSVGIconPainter::setDrawWidth(int dwidth)
+{
+ d->drawWidth = dwidth;
+}
+
+void KSVGIconPainter::setDrawHeight(int dheight)
+{
+ d->drawHeight = dheight;
+}
+
+void KSVGIconPainter::finish()
+{
+ d->helper->blit();
+}
+
+TQImage *KSVGIconPainter::image()
+{
+ return new TQImage(*d->helper->m_image);
+}
+
+TQWMatrix *KSVGIconPainter::tqworldMatrix()
+{
+ return d->helper->m_tqworldMatrix;
+}
+
+void KSVGIconPainter::setWorldMatrix(TQWMatrix *matrix)
+{
+ if(d->helper->m_tqworldMatrix)
+ delete d->helper->m_tqworldMatrix;
+
+ d->helper->m_tqworldMatrix = matrix;
+}
+
+void KSVGIconPainter::setStrokeWidth(double width)
+{
+ d->helper->m_strokeWidth = width;
+}
+
+void KSVGIconPainter::setStrokeMiterLimit(const TQString &miter)
+{
+ d->helper->m_strokeMiterLimit = miter.toInt();
+}
+
+void KSVGIconPainter::setStrokeDashOffset(const TQString &dashOffset)
+{
+ d->helper->m_dashOffset = dashOffset.toUInt();
+}
+
+void KSVGIconPainter::setStrokeDashArray(const TQString &dashes)
+{
+ d->helper->m_dashes = dashes;
+}
+
+void KSVGIconPainter::setCapStyle(const TQString &cap)
+{
+ d->helper->m_capStyle = cap;
+}
+
+void KSVGIconPainter::setJoinStyle(const TQString &join)
+{
+ d->helper->m_joinStyle = join;
+}
+
+void KSVGIconPainter::setStrokeColor(const TQString &stroke)
+{
+ if(stroke.startsWith("url"))
+ {
+ d->helper->m_useStroke = false;
+ d->helper->m_useStrokeGradient = true;
+
+ TQString url = stroke;
+
+ unsigned int start = url.find("#") + 1;
+ unsigned int end = url.findRev(")");
+
+ d->helper->m_strokeGradientReference = url.mid(start, end - start);
+ }
+ else
+ {
+ d->helper->m_strokeColor = parseColor(stroke);
+
+ d->helper->m_useStrokeGradient = false;
+ d->helper->m_strokeGradientReference = TQString::null;
+
+ if(stroke.stripWhiteSpace().lower() != "none")
+ setUseStroke(true);
+ else
+ setUseStroke(false);
+ }
+}
+
+void KSVGIconPainter::setFillColor(const TQString &fill)
+{
+ if(fill.startsWith("url"))
+ {
+ d->helper->m_useFill = false;
+ d->helper->m_useFillGradient = true;
+
+ TQString url = fill;
+
+ unsigned int start = url.find("#") + 1;
+ unsigned int end = url.findRev(")");
+
+ d->helper->m_fillGradientReference = url.mid(start, end - start);
+ }
+ else
+ {
+ d->helper->m_fillColor = parseColor(fill);
+
+ d->helper->m_useFillGradient = false;
+ d->helper->m_fillGradientReference = TQString::null;
+
+ if(fill.stripWhiteSpace().lower() != "none")
+ setUseFill(true);
+ else
+ setUseFill(false);
+ }
+}
+
+void KSVGIconPainter::setFillRule(const TQString &fillRule)
+{
+ d->helper->m_fillRule = fillRule;
+}
+
+TQ_UINT32 KSVGIconPainter::parseOpacity(const TQString &data)
+{
+ int opacity = 255;
+
+ if(!data.isEmpty())
+ {
+ double temp;
+
+ if(data.contains("%"))
+ {
+ TQString tempString = data.left(data.length() - 1);
+ temp = double(255 * tempString.toDouble()) / 100.0;
+ }
+ else
+ temp = data.toDouble();
+
+ opacity = (int) floor(temp * 255 + 0.5);
+ }
+
+ return opacity;
+}
+
+void KSVGIconPainter::setFillOpacity(const TQString &fillOpacity)
+{
+ d->helper->m_fillOpacity = parseOpacity(fillOpacity);
+}
+
+void KSVGIconPainter::setStrokeOpacity(const TQString &strokeOpacity)
+{
+ d->helper->m_strokeOpacity = parseOpacity(strokeOpacity);
+}
+
+void KSVGIconPainter::setOpacity(const TQString &opacity)
+{
+ d->helper->m_opacity = parseOpacity(opacity);
+}
+
+void KSVGIconPainter::setUseFill(bool fill)
+{
+ d->helper->m_useFill = fill;
+}
+
+void KSVGIconPainter::setUseStroke(bool stroke)
+{
+ d->helper->m_useStroke = stroke;
+}
+
+void KSVGIconPainter::setClippingRect(int x, int y, int w, int h)
+{
+ ArtVpath *vec = d->helper->allocVPath(6);
+
+ vec[0].code = ART_MOVETO;
+ vec[0].x = x;
+ vec[0].y = y;
+
+ vec[1].code = ART_LINETO;
+ vec[1].x = x;
+ vec[1].y = y + h;
+
+ vec[2].code = ART_LINETO;
+ vec[2].x = x + w;
+ vec[2].y = y + h;
+
+ vec[3].code = ART_LINETO;
+ vec[3].x = x + w;
+ vec[3].y = y;
+
+ vec[4].code = ART_LINETO;
+ vec[4].x = x;
+ vec[4].y = y;
+
+ vec[5].code = ART_END;
+
+ if(d->helper->m_clipSVP)
+ art_svp_free(d->helper->m_clipSVP);
+
+ d->helper->m_clipSVP = art_svp_from_vpath(vec);
+
+ art_free(vec);
+}
+
+void KSVGIconPainter::drawRectangle(double x, double y, double w, double h, double rx, double ry)
+{
+ if((int) rx != 0 && (int) ry != 0)
+ {
+ ArtVpath *res;
+ ArtBpath *vec = d->helper->allocBPath(10);
+
+ int i = 0;
+
+ if(rx > w / 2)
+ rx = w / 2;
+
+ if(ry > h / 2)
+ ry = h / 2;
+
+ vec[i].code = ART_MOVETO_OPEN;
+ vec[i].x3 = x + rx;
+ vec[i].y3 = y;
+
+ i++;
+
+ vec[i].code = ART_CURVETO;
+ vec[i].x1 = x + rx * (1 - 0.552);
+ vec[i].y1 = y;
+ vec[i].x2 = x;
+ vec[i].y2 = y + ry * (1 - 0.552);
+ vec[i].x3 = x;
+ vec[i].y3 = y + ry;
+
+ i++;
+
+ if(ry < h / 2)
+ {
+ vec[i].code = ART_LINETO;
+ vec[i].x3 = x;
+ vec[i].y3 = y + h - ry;
+
+ i++;
+ }
+
+ vec[i].code = ART_CURVETO;
+ vec[i].x1 = x;
+ vec[i].y1 = y + h - ry * (1 - 0.552);
+ vec[i].x2 = x + rx * (1 - 0.552);
+ vec[i].y2 = y + h;
+ vec[i].x3 = x + rx;
+ vec[i].y3 = y + h;
+
+ i++;
+
+ if(rx < w / 2)
+ {
+ vec[i].code = ART_LINETO;
+ vec[i].x3 = x + w - rx;
+ vec[i].y3 = y + h;
+
+ i++;
+ }
+
+ vec[i].code = ART_CURVETO;
+ vec[i].x1 = x + w - rx * (1 - 0.552);
+ vec[i].y1 = y + h;
+ vec[i].x2 = x + w;
+ vec[i].y2 = y + h - ry * (1 - 0.552);
+ vec[i].x3 = x + w;
+
+ vec[i].y3 = y + h - ry;
+
+ i++;
+
+ if(ry < h / 2)
+ {
+ vec[i].code = ART_LINETO;
+ vec[i].x3 = x + w;
+ vec[i].y3 = y + ry;
+
+ i++;
+ }
+
+ vec[i].code = ART_CURVETO;
+ vec[i].x1 = x + w;
+ vec[i].y1 = y + ry * (1 - 0.552);
+ vec[i].x2 = x + w - rx * (1 - 0.552);
+ vec[i].y2 = y;
+ vec[i].x3 = x + w - rx;
+ vec[i].y3 = y;
+
+ i++;
+
+ if(rx < w / 2)
+ {
+ vec[i].code = ART_LINETO;
+ vec[i].x3 = x + rx;
+ vec[i].y3 = y;
+
+ i++;
+ }
+
+ vec[i].code = ART_END;
+
+ res = d->helper->art_bez_path_to_vec(vec, 0.25);
+ art_free(vec);
+ d->helper->drawVPath(res);
+ }
+ else
+ {
+ ArtVpath *vec = d->helper->allocVPath(6);
+
+ vec[0].code = ART_MOVETO;
+ vec[0].x = x;
+ vec[0].y = y;
+
+ vec[1].code = ART_LINETO;
+ vec[1].x = x;
+ vec[1].y = y + h;
+
+ vec[2].code = ART_LINETO;
+ vec[2].x = x + w;
+ vec[2].y = y + h;
+
+ vec[3].code = ART_LINETO;
+ vec[3].x = x + w;
+ vec[3].y = y;
+
+ vec[4].code = ART_LINETO;
+ vec[4].x = x;
+ vec[4].y = y;
+
+ vec[5].code = ART_END;
+
+ d->helper->drawVPath(vec);
+ }
+}
+
+void KSVGIconPainter::drawEllipse(double cx, double cy, double rx, double ry)
+{
+ ArtBpath *temp;
+
+ temp = d->helper->allocBPath(6);
+
+ double x1, y1, x2, y2, x3, y3;
+ double len = 0.55228474983079356;
+ double cos4[] = {1.0, 0.0, -1.0, 0.0, 1.0};
+ double sin4[] = {0.0, 1.0, 0.0, -1.0, 0.0};
+ int i = 0;
+
+ temp[i].code = ART_MOVETO;
+ temp[i].x3 = cx + rx;
+ temp[i].y3 = cy;
+
+ i++;
+
+ while(i < 5)
+ {
+ x1 = cos4[i-1] + len * cos4[i];
+ y1 = sin4[i-1] + len * sin4[i];
+ x2 = cos4[i] + len * cos4[i-1];
+ y2 = sin4[i] + len * sin4[i-1];
+ x3 = cos4[i];
+ y3 = sin4[i];
+
+ temp[i].code = ART_CURVETO;
+ temp[i].x1 = cx + x1 * rx;
+ temp[i].y1 = cy + y1 * ry;
+ temp[i].x2 = cx + x2 * rx;
+ temp[i].y2 = cy + y2 * ry;
+ temp[i].x3 = cx + x3 * rx;
+ temp[i].y3 = cy + y3 * ry;
+
+ i++;
+ }
+
+ temp[i].code = ART_END;
+
+ d->helper->drawBPath(temp);
+
+ art_free(temp);
+}
+
+void KSVGIconPainter::drawLine(double x1, double y1, double x2, double y2)
+{
+ ArtVpath *vec;
+
+ vec = d->helper->allocVPath(3);
+
+ vec[0].code = ART_MOVETO_OPEN;
+ vec[0].x = x1;
+ vec[0].y = y1;
+
+ vec[1].code = ART_LINETO;
+ vec[1].x = x2;
+ vec[1].y = y2;
+
+ vec[2].code = ART_END;
+
+ d->helper->drawVPath(vec);
+}
+
+void KSVGIconPainter::drawPolyline(TQPointArray polyArray, int points)
+{
+ if(polyArray.point(0).x() == -1 || polyArray.point(0).y() == -1)
+ return;
+
+ ArtVpath *polyline;
+
+ if(points == -1)
+ points = polyArray.count();
+
+ polyline = d->helper->allocVPath(3 + points);
+ polyline[0].code = ART_MOVETO;
+ polyline[0].x = polyArray.point(0).x();
+ polyline[0].y = polyArray.point(0).y();
+
+ int index;
+ for(index = 1; index < points; index++)
+ {
+ TQPoint point = polyArray.point(index);
+ polyline[index].code = ART_LINETO;
+ polyline[index].x = point.x();
+ polyline[index].y = point.y();
+ }
+
+ if(d->helper->m_useFill) // if the polyline must be filled, inform libart that it should not be closed.
+ {
+ polyline[index].code = (ArtPathcode)ART_END2;
+ polyline[index].x = polyArray.point(0).x();
+ polyline[index++].y = polyArray.point(0).y();
+ }
+
+ polyline[index].code = ART_END;
+
+ d->helper->drawVPath(polyline);
+}
+
+void KSVGIconPainter::drawPolygon(TQPointArray polyArray)
+{
+ ArtVpath *polygon;
+
+ polygon = d->helper->allocVPath(3 + polyArray.count());
+ polygon[0].code = ART_MOVETO;
+ polygon[0].x = polyArray.point(0).x();
+ polygon[0].y = polyArray.point(0).y();
+
+ unsigned int index;
+ for(index = 1; index < polyArray.count(); index++)
+ {
+ TQPoint point = polyArray.point(index);
+ polygon[index].code = ART_LINETO;
+ polygon[index].x = point.x();
+ polygon[index].y = point.y();
+ }
+
+ polygon[index].code = ART_LINETO;
+ polygon[index].x = polyArray.point(0).x();
+ polygon[index].y = polyArray.point(0).y();
+
+ index++;
+ polygon[index].code = ART_END;
+
+ d->helper->drawVPath(polygon);
+}
+
+// Path parsing tool
+// parses the coord into number and forwards to the next token
+static const char *getCoord(const char *ptr, double &number)
+{
+ int integer, exponent;
+ double decimal, frac;
+ int sign, expsign;
+
+ exponent = 0;
+ integer = 0;
+ frac = 1.0;
+ decimal = 0;
+ sign = 1;
+ expsign = 1;
+
+ // read the sign
+ if(*ptr == '+')
+ ptr++;
+ else if(*ptr == '-')
+ {
+ ptr++;
+ sign = -1;
+ }
+ // read the integer part
+ while(*ptr != '\0' && *ptr >= '0' && *ptr <= '9')
+ integer = (integer * 10) + *(ptr++) - '0';
+
+ if(*ptr == '.') // read the decimals
+ {
+ ptr++;
+ while(*ptr != '\0' && *ptr >= '0' && *ptr <= '9')
+ decimal += (*(ptr++) - '0') * (frac *= 0.1);
+ }
+
+ if(*ptr == 'e' || *ptr == 'E') // read the exponent part
+ {
+ ptr++;
+
+ // read the sign of the exponent
+ if(*ptr == '+')
+ ptr++;
+ else if(*ptr == '-')
+ {
+ ptr++;
+ expsign = -1;
+ }
+
+ exponent = 0;
+ while(*ptr != '\0' && *ptr >= '0' && *ptr <= '9')
+ {
+ exponent *= 10;
+ exponent += *ptr - '0';
+ ptr++;
+ }
+ }
+
+ number = integer + decimal;
+ number *= sign * pow(10.0, expsign * exponent);
+
+ // skip the following space
+ if(*ptr == ' ')
+ ptr++;
+
+ return ptr;
+}
+
+void KSVGIconPainter::drawPath(const TQString &data, bool filled)
+{
+ if (!data.isEmpty())
+ {
+ TQString value = data;
+
+ TQMemArray<ArtBpath> vec;
+ int index = -1;
+
+ double curx = 0.0, cury = 0.0, contrlx = 0.0, contrly = 0.0, xc, yc;
+ unsigned int lastCommand = 0;
+
+ TQString _d = value.replace(",", " ");
+ _d = _d.simplifyWhiteSpace();
+ const char *ptr = _d.latin1();
+ const char *end = _d.latin1() + _d.length() + 1;
+
+ double tox, toy, x1, y1, x2, y2, rx, ry, angle;
+ bool largeArc, sweep;
+ char command = *(ptr++);
+
+ while(ptr < end)
+ {
+ if(*ptr == ' ')
+ ptr++;
+
+ switch(command)
+ {
+ case 'm':
+ ptr = getCoord(ptr, tox);
+ ptr = getCoord(ptr, toy);
+
+ if(index != -1 && lastCommand != 'z')
+ {
+ // Find last subpath
+ int find = -1;
+ for(int i = index; i >= 0; i--)
+ {
+ if(vec[i].code == ART_MOVETO_OPEN || vec[i].code == ART_MOVETO)
+ {
+ find = i;
+ break;
+ }
+ }
+
+ index++;
+
+ if(vec.size() == (unsigned int) index)
+ vec.resize(index + 1);
+
+ vec[index].code = (ArtPathcode)ART_END2;
+ vec[index].x3 = vec[find].x3;
+ vec[index].y3 = vec[find].y3;
+ }
+
+ curx += tox;
+ cury += toy;
+
+ index++;
+
+ d->helper->ensureSpace(vec, index);
+
+ vec[index].code = (index == 0) ? ART_MOVETO : ART_MOVETO_OPEN;
+ vec[index].x3 = curx;
+ vec[index].y3 = cury;
+
+ lastCommand = 'm';
+ break;
+ case 'M':
+ ptr = getCoord(ptr, tox);
+ ptr = getCoord(ptr, toy);
+ if(index != -1 && lastCommand != 'z')
+ {
+ // Find last subpath
+ int find = -1;
+ for(int i = index; i >= 0; i--)
+ {
+ if(vec[i].code == ART_MOVETO_OPEN || vec[i].code == ART_MOVETO)
+ {
+ find = i;
+ break;
+ }
+ }
+
+ index++;
+
+ if(vec.size() == (unsigned int) index)
+ vec.resize(index + 1);
+
+ vec[index].code = (ArtPathcode)ART_END2;
+ vec[index].x3 = vec[find].x3;
+ vec[index].y3 = vec[find].y3;
+ }
+
+ curx = tox;
+ cury = toy;
+
+ index++;
+
+ d->helper->ensureSpace(vec, index);
+
+ vec[index].code = (index == 0) ? ART_MOVETO : ART_MOVETO_OPEN;
+ vec[index].x3 = curx;
+ vec[index].y3 = cury;
+
+ lastCommand = 'M';
+ break;
+ case 'l':
+ ptr = getCoord(ptr, tox);
+ ptr = getCoord(ptr, toy);
+
+ index++;
+
+ d->helper->ensureSpace(vec, index);
+
+ vec[index].code = ART_LINETO;
+ vec[index].x3 = curx + tox;
+ vec[index].y3 = cury + toy;
+
+ curx += tox;
+ cury += toy;
+
+ lastCommand = 'l';
+ break;
+ case 'L':
+ ptr = getCoord(ptr, tox);
+ ptr = getCoord(ptr, toy);
+
+ index++;
+
+ d->helper->ensureSpace(vec, index);
+
+ vec[index].code = ART_LINETO;
+ vec[index].x3 = tox;
+ vec[index].y3 = toy;
+
+ curx = tox;
+ cury = toy;
+
+ lastCommand = 'L';
+ break;
+ case 'h':
+ ptr = getCoord(ptr, tox);
+
+ index++;
+
+ curx += tox;
+
+ d->helper->ensureSpace(vec, index);
+
+ vec[index].code = ART_LINETO;
+ vec[index].x3 = curx;
+ vec[index].y3 = cury;
+
+ lastCommand = 'h';
+ break;
+ case 'H':
+ ptr = getCoord(ptr, tox);
+
+ index++;
+
+ curx = tox;
+
+ d->helper->ensureSpace(vec, index);
+
+ vec[index].code = ART_LINETO;
+ vec[index].x3 = curx;
+ vec[index].y3 = cury;
+
+ lastCommand = 'H';
+ break;
+ case 'v':
+ ptr = getCoord(ptr, toy);
+
+ index++;
+
+ cury += toy;
+
+ d->helper->ensureSpace(vec, index);
+
+ vec[index].code = ART_LINETO;
+ vec[index].x3 = curx;
+ vec[index].y3 = cury;
+
+ lastCommand = 'v';
+ break;
+ case 'V':
+ ptr = getCoord(ptr, toy);
+
+ index++;
+
+ cury = toy;
+
+ d->helper->ensureSpace(vec, index);
+
+ vec[index].code = ART_LINETO;
+ vec[index].x3 = curx;
+ vec[index].y3 = cury;
+
+ lastCommand = 'V';
+ break;
+ case 'c':
+ ptr = getCoord(ptr, x1);
+ ptr = getCoord(ptr, y1);
+ ptr = getCoord(ptr, x2);
+ ptr = getCoord(ptr, y2);
+ ptr = getCoord(ptr, tox);
+ ptr = getCoord(ptr, toy);
+
+ index++;
+
+ d->helper->ensureSpace(vec, index);
+
+ vec[index].code = ART_CURVETO;
+ vec[index].x1 = curx + x1;
+ vec[index].y1 = cury + y1;
+ vec[index].x2 = curx + x2;
+ vec[index].y2 = cury + y2;
+ vec[index].x3 = curx + tox;
+ vec[index].y3 = cury + toy;
+
+ curx += tox;
+ cury += toy;
+
+ contrlx = vec[index].x2;
+ contrly = vec[index].y2;
+
+ lastCommand = 'c';
+ break;
+ case 'C':
+ ptr = getCoord(ptr, x1);
+ ptr = getCoord(ptr, y1);
+ ptr = getCoord(ptr, x2);
+ ptr = getCoord(ptr, y2);
+ ptr = getCoord(ptr, tox);
+ ptr = getCoord(ptr, toy);
+
+ index++;
+
+ d->helper->ensureSpace(vec, index);
+
+ vec[index].code = ART_CURVETO;
+ vec[index].x1 = x1;
+ vec[index].y1 = y1;
+ vec[index].x2 = x2;
+ vec[index].y2 = y2;
+ vec[index].x3 = tox;
+ vec[index].y3 = toy;
+
+ curx = vec[index].x3;
+ cury = vec[index].y3;
+ contrlx = vec[index].x2;
+ contrly = vec[index].y2;
+
+ lastCommand = 'C';
+ break;
+ case 's':
+ ptr = getCoord(ptr, x2);
+ ptr = getCoord(ptr, y2);
+ ptr = getCoord(ptr, tox);
+ ptr = getCoord(ptr, toy);
+
+ index++;
+
+ d->helper->ensureSpace(vec, index);
+
+ vec[index].code = ART_CURVETO;
+ vec[index].x1 = 2 * curx - contrlx;
+ vec[index].y1 = 2 * cury - contrly;
+ vec[index].x2 = curx + x2;
+ vec[index].y2 = cury + y2;
+ vec[index].x3 = curx + tox;
+ vec[index].y3 = cury + toy;
+
+ curx += tox;
+ cury += toy;
+
+ contrlx = vec[index].x2;
+ contrly = vec[index].y2;
+
+ lastCommand = 's';
+ break;
+ case 'S':
+ ptr = getCoord(ptr, x2);
+ ptr = getCoord(ptr, y2);
+ ptr = getCoord(ptr, tox);
+ ptr = getCoord(ptr, toy);
+
+ index++;
+
+ d->helper->ensureSpace(vec, index);
+
+ vec[index].code = ART_CURVETO;
+ vec[index].x1 = 2 * curx - contrlx;
+ vec[index].y1 = 2 * cury - contrly;
+ vec[index].x2 = x2;
+ vec[index].y2 = y2;
+ vec[index].x3 = tox;
+ vec[index].y3 = toy;
+
+ curx = vec[index].x3;
+ cury = vec[index].y3;
+ contrlx = vec[index].x2;
+ contrly = vec[index].y2;
+
+ lastCommand = 'S';
+ break;
+ case 'q':
+ ptr = getCoord(ptr, x1);
+ ptr = getCoord(ptr, y1);
+ ptr = getCoord(ptr, tox);
+ ptr = getCoord(ptr, toy);
+
+ index++;
+
+ d->helper->ensureSpace(vec, index);
+
+ vec[index].code = ART_CURVETO;
+ vec[index].x1 = (curx + 2 * (x1 + curx)) * (1.0 / 3.0);
+ vec[index].y1 = (cury + 2 * (y1 + cury)) * (1.0 / 3.0);
+ vec[index].x2 = ((curx + tox) + 2 * (x1 + curx)) * (1.0 / 3.0);
+ vec[index].y2 = ((cury + toy) + 2 * (y1 + cury)) * (1.0 / 3.0);
+ vec[index].x3 = curx + tox;
+ vec[index].y3 = cury + toy;
+
+ contrlx = curx + x1;
+ contrly = cury + y1;
+ curx += tox;
+ cury += toy;
+
+ lastCommand = 'q';
+ break;
+ case 'Q':
+ ptr = getCoord(ptr, x1);
+ ptr = getCoord(ptr, y1);
+ ptr = getCoord(ptr, tox);
+ ptr = getCoord(ptr, toy);
+
+ index++;
+
+ d->helper->ensureSpace(vec, index);
+
+ // TODO : if this fails make it more like QuadraticRel
+ vec[index].code = ART_CURVETO;
+ vec[index].x1 = (curx + 2 * x1) * (1.0 / 3.0);
+ vec[index].y1 = (cury + 2 * y1) * (1.0 / 3.0);
+ vec[index].x2 = (tox + 2 * x1) * (1.0 / 3.0);
+ vec[index].y2 = (toy + 2 * y1) * (1.0 / 3.0);
+ vec[index].x3 = tox;
+ vec[index].y3 = toy;
+
+ curx = vec[index].x3;
+ cury = vec[index].y3;
+ contrlx = vec[index].x2;
+ contrly = vec[index].y2;
+
+ lastCommand = 'Q';
+ break;
+ case 't':
+ ptr = getCoord(ptr, tox);
+ ptr = getCoord(ptr, toy);
+
+ xc = 2 * curx - contrlx;
+ yc = 2 * cury - contrly;
+
+ index++;
+
+ d->helper->ensureSpace(vec, index);
+
+ vec[index].code = ART_CURVETO;
+ vec[index].x1 = (curx + 2 * xc) * (1.0 / 3.0);
+ vec[index].y1 = (cury + 2 * yc) * (1.0 / 3.0);
+ vec[index].x2 = ((curx + tox) + 2 * xc) * (1.0 / 3.0);
+ vec[index].y2 = ((cury + toy) + 2 * yc) * (1.0 / 3.0);
+
+ vec[index].x3 = curx + tox;
+ vec[index].y3 = cury + toy;
+
+ curx += tox;
+ cury += toy;
+ contrlx = xc;
+ contrly = yc;
+
+ lastCommand = 't';
+ break;
+ case 'T':
+ ptr = getCoord(ptr, tox);
+ ptr = getCoord(ptr, toy);
+
+ xc = 2 * curx - contrlx;
+ yc = 2 * cury - contrly;
+
+ index++;
+
+ d->helper->ensureSpace(vec, index);
+
+ vec[index].code = ART_CURVETO;
+ vec[index].x1 = (curx + 2 * xc) * (1.0 / 3.0);
+ vec[index].y1 = (cury + 2 * yc) * (1.0 / 3.0);
+ vec[index].x2 = (tox + 2 * xc) * (1.0 / 3.0);
+ vec[index].y2 = (toy + 2 * yc) * (1.0 / 3.0);
+ vec[index].x3 = tox;
+ vec[index].y3 = toy;
+
+ curx = tox;
+ cury = toy;
+ contrlx = xc;
+ contrly = yc;
+
+ lastCommand = 'T';
+ break;
+ case 'z':
+ case 'Z':
+ int find;
+ find = -1;
+ for(int i = index; i >= 0; i--)
+ {
+ if(vec[i].code == ART_MOVETO_OPEN || vec[i].code == ART_MOVETO)
+ {
+ find = i;
+ break;
+ }
+ }
+
+ if(find != -1)
+ {
+ if(vec[find].x3 != curx || vec[find].y3 != cury)
+ {
+ index++;
+
+ d->helper->ensureSpace(vec, index);
+
+ vec[index].code = ART_LINETO;
+ vec[index].x3 = vec[find].x3;
+ vec[index].y3 = vec[find].y3;
+ }
+ }
+
+ // reset for next (sub)path
+ curx = vec[find].x3;
+ cury = vec[find].y3;
+
+ lastCommand = 'z';
+ break;
+ case 'a':
+ ptr = getCoord(ptr, rx);
+ ptr = getCoord(ptr, ry);
+ ptr = getCoord(ptr, angle);
+ ptr = getCoord(ptr, tox);
+ largeArc = tox == 1;
+ ptr = getCoord(ptr, tox);
+ sweep = tox == 1;
+ ptr = getCoord(ptr, tox);
+ ptr = getCoord(ptr, toy);
+
+ // Spec: radii are nonnegative numbers
+ rx = fabs(rx);
+ ry = fabs(ry);
+
+ d->helper->calculateArc(true, vec, index, curx, cury, angle, tox, toy, rx, ry, largeArc, sweep);
+
+ lastCommand = 'a';
+ break;
+ case 'A':
+ ptr = getCoord(ptr, rx);
+ ptr = getCoord(ptr, ry);
+ ptr = getCoord(ptr, angle);
+ ptr = getCoord(ptr, tox);
+ largeArc = tox == 1;
+ ptr = getCoord(ptr, tox);
+ sweep = tox == 1;
+ ptr = getCoord(ptr, tox);
+ ptr = getCoord(ptr, toy);
+
+ // Spec: radii are nonnegative numbers
+ rx = fabs(rx);
+ ry = fabs(ry);
+
+ d->helper->calculateArc(false, vec, index, curx, cury, angle, tox, toy, rx, ry, largeArc, sweep);
+
+ lastCommand = 'A';
+ break;
+ }
+
+ if(*ptr == '+' || *ptr == '-' || (*ptr >= '0' && *ptr <= '9'))
+ {
+ // there are still coords in this command
+ if(command == 'M')
+ command = 'L';
+ else if(command == 'm')
+ command = 'l';
+ }
+ else
+ command = *(ptr++);
+
+ // Detect reflection points
+ if(lastCommand != 'C' && lastCommand != 'c' &&
+ lastCommand != 'S' && lastCommand != 's' &&
+ lastCommand != 'Q' && lastCommand != 'q' &&
+ lastCommand != 'T' && lastCommand != 't')
+ {
+ contrlx = curx;
+ contrly = cury;
+ }
+ }
+
+ // Find last subpath
+ int find = -1;
+ for(int i = index; i >= 0; i--)
+ {
+ if(vec[i].code == ART_MOVETO_OPEN || vec[i].code == ART_MOVETO)
+ {
+ find = i;
+ break;
+ }
+ }
+
+ // Fix a problem where the .svg file used doubles as values... (sofico.svg)
+ if(curx != vec[find].x3 && cury != vec[find].y3)
+ {
+ if((int) curx == (int) vec[find].x3 && (int) cury == (int) vec[find].y3)
+ {
+ index++;
+
+ if(vec.size() == (unsigned int) index)
+ vec.resize(index + 1);
+
+ vec[index].code = ART_LINETO;
+ vec[index].x3 = vec[find].x3;
+ vec[index].y3 = vec[find].y3;
+
+ curx = vec[find].x3;
+ cury = vec[find].y3;
+ }
+ }
+
+ // Handle filled paths that are not closed explicitly
+ if(filled)
+ {
+ if((int) curx != (int) vec[find].x3 || (int) cury != (int) vec[find].y3)
+ {
+ index++;
+
+ if(vec.size() == (unsigned int) index)
+ vec.resize(index + 1);
+
+ vec[index].code = (ArtPathcode)ART_END2;
+ vec[index].x3 = vec[find].x3;
+ vec[index].y3 = vec[find].y3;
+
+ curx = vec[find].x3;
+ cury = vec[find].y3;
+ }
+ }
+
+ // Close
+ index++;
+
+ if(vec.size() == (unsigned int) index)
+ vec.resize(index + 1);
+
+ vec[index].code = ART_END;
+
+ // There are pure-moveto paths which reference paint servers *bah*
+ // Do NOT render them
+ bool render = false;
+ for(int i = index; i >= 0; i--)
+ {
+ if(vec[i].code != ART_MOVETO_OPEN && vec[i].code != ART_MOVETO && !(vec[i].code >= ART_END))
+ {
+ render = true;
+ break;
+ }
+ }
+
+ if(render)
+ d->helper->drawBPath(vec.data());
+ }
+}
+
+void KSVGIconPainter::drawImage(double x, double y, TQImage &image)
+{
+ if(image.depth() != 32)
+ image = image.convertDepth(32);
+
+ double affine[6];
+ affine[0] = d->helper->m_tqworldMatrix->m11();
+ affine[1] = d->helper->m_tqworldMatrix->m12();
+ affine[2] = d->helper->m_tqworldMatrix->m21();
+ affine[3] = d->helper->m_tqworldMatrix->m22();
+ // use the world matrix to convert the coordinates
+ d->helper->m_tqworldMatrix->map(x, y, &affine[4], &affine[5]);
+
+ d->helper->art_rgba_rgba_affine(d->helper->m_buffer, 0, 0, d->helper->m_width, d->helper->m_height,
+ d->helper->m_rowstride, image.bits(), image.width(), image.height(),
+ image.width() * 4, affine);
+}
+
+TQColor KSVGIconPainter::parseColor(const TQString &param)
+{
+ if(param.stripWhiteSpace().startsWith("#"))
+ {
+ TQColor color;
+ color.setNamedColor(param.stripWhiteSpace());
+ return color;
+ }
+ else if(param.stripWhiteSpace().startsWith("rgb("))
+ {
+ TQString parse = param.stripWhiteSpace();
+ TQStringList colors = TQStringList::split(',', parse);
+ TQString r = colors[0].right((colors[0].length() - 4));
+ TQString g = colors[1];
+ TQString b = colors[2].left((colors[2].length() - 1));
+
+ if(r.contains("%"))
+ {
+ r = r.left(r.length() - 1);
+ r = TQString::number(int((double(255 * r.toDouble()) / 100.0)));
+ }
+
+ if(g.contains("%"))
+ {
+ g = g.left(g.length() - 1);
+ g = TQString::number(int((double(255 * g.toDouble()) / 100.0)));
+ }
+
+ if(b.contains("%"))
+ {
+ b = b.left(b.length() - 1);
+ b = TQString::number(int((double(255 * b.toDouble()) / 100.0)));
+ }
+
+ return TQColor(r.toInt(), g.toInt(), b.toInt());
+ }
+ else
+ {
+ TQString rgbColor = param.stripWhiteSpace();
+
+ if(rgbColor == "aliceblue")
+ return TQColor(240, 248, 255);
+ else if(rgbColor == "antiquewhite")
+ return TQColor(250, 235, 215);
+ else if(rgbColor == "aqua")
+ return TQColor(0, 255, 255);
+ else if(rgbColor == "aquamarine")
+ return TQColor(127, 255, 212);
+ else if(rgbColor == "azure")
+ return TQColor(240, 255, 255);
+ else if(rgbColor == "beige")
+ return TQColor(245, 245, 220);
+ else if(rgbColor == "bisque")
+ return TQColor(255, 228, 196);
+ else if(rgbColor == "black")
+ return TQColor(0, 0, 0);
+ else if(rgbColor == "blanchedalmond")
+ return TQColor(255, 235, 205);
+ else if(rgbColor == "blue")
+ return TQColor(0, 0, 255);
+ else if(rgbColor == "blueviolet")
+ return TQColor(138, 43, 226);
+ else if(rgbColor == "brown")
+ return TQColor(165, 42, 42);
+ else if(rgbColor == "burlywood")
+ return TQColor(222, 184, 135);
+ else if(rgbColor == "cadetblue")
+ return TQColor(95, 158, 160);
+ else if(rgbColor == "chartreuse")
+ return TQColor(127, 255, 0);
+ else if(rgbColor == "chocolate")
+ return TQColor(210, 105, 30);
+ else if(rgbColor == "coral")
+ return TQColor(255, 127, 80);
+ else if(rgbColor == "cornflowerblue")
+ return TQColor(100, 149, 237);
+ else if(rgbColor == "cornsilk")
+ return TQColor(255, 248, 220);
+ else if(rgbColor == "crimson")
+ return TQColor(220, 20, 60);
+ else if(rgbColor == "cyan")
+ return TQColor(0, 255, 255);
+ else if(rgbColor == "darkblue")
+ return TQColor(0, 0, 139);
+ else if(rgbColor == "darkcyan")
+ return TQColor(0, 139, 139);
+ else if(rgbColor == "darkgoldenrod")
+ return TQColor(184, 134, 11);
+ else if(rgbColor == "darkgray")
+ return TQColor(169, 169, 169);
+ else if(rgbColor == "darkgrey")
+ return TQColor(169, 169, 169);
+ else if(rgbColor == "darkgreen")
+ return TQColor(0, 100, 0);
+ else if(rgbColor == "darkkhaki")
+ return TQColor(189, 183, 107);
+ else if(rgbColor == "darkmagenta")
+ return TQColor(139, 0, 139);
+ else if(rgbColor == "darkolivegreen")
+ return TQColor(85, 107, 47);
+ else if(rgbColor == "darkorange")
+ return TQColor(255, 140, 0);
+ else if(rgbColor == "darkorchid")
+ return TQColor(153, 50, 204);
+ else if(rgbColor == "darkred")
+ return TQColor(139, 0, 0);
+ else if(rgbColor == "darksalmon")
+ return TQColor(233, 150, 122);
+ else if(rgbColor == "darkseagreen")
+ return TQColor(143, 188, 143);
+ else if(rgbColor == "darkslateblue")
+ return TQColor(72, 61, 139);
+ else if(rgbColor == "darkslategray")
+ return TQColor(47, 79, 79);
+ else if(rgbColor == "darkslategrey")
+ return TQColor(47, 79, 79);
+ else if(rgbColor == "darkturquoise")
+ return TQColor(0, 206, 209);
+ else if(rgbColor == "darkviolet")
+ return TQColor(148, 0, 211);
+ else if(rgbColor == "deeppink")
+ return TQColor(255, 20, 147);
+ else if(rgbColor == "deepskyblue")
+ return TQColor(0, 191, 255);
+ else if(rgbColor == "dimgray")
+ return TQColor(105, 105, 105);
+ else if(rgbColor == "dimgrey")
+ return TQColor(105, 105, 105);
+ else if(rgbColor == "dodgerblue")
+ return TQColor(30, 144, 255);
+ else if(rgbColor == "firebrick")
+ return TQColor(178, 34, 34);
+ else if(rgbColor == "floralwhite")
+ return TQColor(255, 250, 240);
+ else if(rgbColor == "forestgreen")
+ return TQColor(34, 139, 34);
+ else if(rgbColor == "fuchsia")
+ return TQColor(255, 0, 255);
+ else if(rgbColor == "gainsboro")
+ return TQColor(220, 220, 220);
+ else if(rgbColor == "ghostwhite")
+ return TQColor(248, 248, 255);
+ else if(rgbColor == "gold")
+ return TQColor(255, 215, 0);
+ else if(rgbColor == "goldenrod")
+ return TQColor(218, 165, 32);
+ else if(rgbColor == "gray")
+ return TQColor(128, 128, 128);
+ else if(rgbColor == "grey")
+ return TQColor(128, 128, 128);
+ else if(rgbColor == "green")
+ return TQColor(0, 128, 0);
+ else if(rgbColor == "greenyellow")
+ return TQColor(173, 255, 47);
+ else if(rgbColor == "honeydew")
+ return TQColor(240, 255, 240);
+ else if(rgbColor == "hotpink")
+ return TQColor(255, 105, 180);
+ else if(rgbColor == "indianred")
+ return TQColor(205, 92, 92);
+ else if(rgbColor == "indigo")
+ return TQColor(75, 0, 130);
+ else if(rgbColor == "ivory")
+ return TQColor(255, 255, 240);
+ else if(rgbColor == "khaki")
+ return TQColor(240, 230, 140);
+ else if(rgbColor == "lavender")
+ return TQColor(230, 230, 250);
+ else if(rgbColor == "lavenderblush")
+ return TQColor(255, 240, 245);
+ else if(rgbColor == "lawngreen")
+ return TQColor(124, 252, 0);
+ else if(rgbColor == "lemonchiffon")
+ return TQColor(255, 250, 205);
+ else if(rgbColor == "lightblue")
+ return TQColor(173, 216, 230);
+ else if(rgbColor == "lightcoral")
+ return TQColor(240, 128, 128);
+ else if(rgbColor == "lightcyan")
+ return TQColor(224, 255, 255);
+ else if(rgbColor == "lightgoldenrodyellow")
+ return TQColor(250, 250, 210);
+ else if(rgbColor == "lightgray")
+ return TQColor(211, 211, 211);
+ else if(rgbColor == "lightgrey")
+ return TQColor(211, 211, 211);
+ else if(rgbColor == "lightgreen")
+ return TQColor(144, 238, 144);
+ else if(rgbColor == "lightpink")
+ return TQColor(255, 182, 193);
+ else if(rgbColor == "lightsalmon")
+ return TQColor(255, 160, 122);
+ else if(rgbColor == "lightseagreen")
+ return TQColor(32, 178, 170);
+ else if(rgbColor == "lightskyblue")
+ return TQColor(135, 206, 250);
+ else if(rgbColor == "lightslategray")
+ return TQColor(119, 136, 153);
+ else if(rgbColor == "lightslategrey")
+ return TQColor(119, 136, 153);
+ else if(rgbColor == "lightsteelblue")
+ return TQColor(176, 196, 222);
+ else if(rgbColor == "lightyellow")
+ return TQColor(255, 255, 224);
+ else if(rgbColor == "lime")
+ return TQColor(0, 255, 0);
+ else if(rgbColor == "limegreen")
+ return TQColor(50, 205, 50);
+ else if(rgbColor == "linen")
+ return TQColor(250, 240, 230);
+ else if(rgbColor == "magenta")
+ return TQColor(255, 0, 255);
+ else if(rgbColor == "maroon")
+ return TQColor(128, 0, 0);
+ else if(rgbColor == "mediumaquamarine")
+ return TQColor(102, 205, 170);
+ else if(rgbColor == "mediumblue")
+ return TQColor(0, 0, 205);
+ else if(rgbColor == "mediumorchid")
+ return TQColor(186, 85, 211);
+ else if(rgbColor == "mediumpurple")
+ return TQColor(147, 112, 219);
+ else if(rgbColor == "mediumseagreen")
+ return TQColor(60, 179, 113);
+ else if(rgbColor == "mediumslateblue")
+ return TQColor(123, 104, 238);
+ else if(rgbColor == "mediumspringgreen")
+ return TQColor(0, 250, 154);
+ else if(rgbColor == "mediumturquoise")
+ return TQColor(72, 209, 204);
+ else if(rgbColor == "mediumvioletred")
+ return TQColor(199, 21, 133);
+ else if(rgbColor == "midnightblue")
+ return TQColor(25, 25, 112);
+ else if(rgbColor == "mintcream")
+ return TQColor(245, 255, 250);
+ else if(rgbColor == "mistyrose")
+ return TQColor(255, 228, 225);
+ else if(rgbColor == "tqmoccasin")
+ return TQColor(255, 228, 181);
+ else if(rgbColor == "navajowhite")
+ return TQColor(255, 222, 173);
+ else if(rgbColor == "navy")
+ return TQColor(0, 0, 128);
+ else if(rgbColor == "oldlace")
+ return TQColor(253, 245, 230);
+ else if(rgbColor == "olive")
+ return TQColor(128, 128, 0);
+ else if(rgbColor == "olivedrab")
+ return TQColor(107, 142, 35);
+ else if(rgbColor == "orange")
+ return TQColor(255, 165, 0);
+ else if(rgbColor == "orangered")
+ return TQColor(255, 69, 0);
+ else if(rgbColor == "orchid")
+ return TQColor(218, 112, 214);
+ else if(rgbColor == "palegoldenrod")
+ return TQColor(238, 232, 170);
+ else if(rgbColor == "palegreen")
+ return TQColor(152, 251, 152);
+ else if(rgbColor == "paleturquoise")
+ return TQColor(175, 238, 238);
+ else if(rgbColor == "palevioletred")
+ return TQColor(219, 112, 147);
+ else if(rgbColor == "papayawhip")
+ return TQColor(255, 239, 213);
+ else if(rgbColor == "peachpuff")
+ return TQColor(255, 218, 185);
+ else if(rgbColor == "peru")
+ return TQColor(205, 133, 63);
+ else if(rgbColor == "pink")
+ return TQColor(255, 192, 203);
+ else if(rgbColor == "plum")
+ return TQColor(221, 160, 221);
+ else if(rgbColor == "powderblue")
+ return TQColor(176, 224, 230);
+ else if(rgbColor == "purple")
+ return TQColor(128, 0, 128);
+ else if(rgbColor == "red")
+ return TQColor(255, 0, 0);
+ else if(rgbColor == "rosybrown")
+ return TQColor(188, 143, 143);
+ else if(rgbColor == "royalblue")
+ return TQColor(65, 105, 225);
+ else if(rgbColor == "saddlebrown")
+ return TQColor(139, 69, 19);
+ else if(rgbColor == "salmon")
+ return TQColor(250, 128, 114);
+ else if(rgbColor == "sandybrown")
+ return TQColor(244, 164, 96);
+ else if(rgbColor == "seagreen")
+ return TQColor(46, 139, 87);
+ else if(rgbColor == "seashell")
+ return TQColor(255, 245, 238);
+ else if(rgbColor == "sienna")
+ return TQColor(160, 82, 45);
+ else if(rgbColor == "silver")
+ return TQColor(192, 192, 192);
+ else if(rgbColor == "skyblue")
+ return TQColor(135, 206, 235);
+ else if(rgbColor == "slateblue")
+ return TQColor(106, 90, 205);
+ else if(rgbColor == "slategray")
+ return TQColor(112, 128, 144);
+ else if(rgbColor == "slategrey")
+ return TQColor(112, 128, 144);
+ else if(rgbColor == "snow")
+ return TQColor(255, 250, 250);
+ else if(rgbColor == "springgreen")
+ return TQColor(0, 255, 127);
+ else if(rgbColor == "steelblue")
+ return TQColor(70, 130, 180);
+ else if(rgbColor == "tan")
+ return TQColor(210, 180, 140);
+ else if(rgbColor == "teal")
+ return TQColor(0, 128, 128);
+ else if(rgbColor == "thistle")
+ return TQColor(216, 191, 216);
+ else if(rgbColor == "tomato")
+ return TQColor(255, 99, 71);
+ else if(rgbColor == "turquoise")
+ return TQColor(64, 224, 208);
+ else if(rgbColor == "violet")
+ return TQColor(238, 130, 238);
+ else if(rgbColor == "wheat")
+ return TQColor(245, 222, 179);
+ else if(rgbColor == "white")
+ return TQColor(255, 255, 255);
+ else if(rgbColor == "whitesmoke")
+ return TQColor(245, 245, 245);
+ else if(rgbColor == "yellow")
+ return TQColor(255, 255, 0);
+ else if(rgbColor == "yellowgreen")
+ return TQColor(154, 205, 50);
+ }
+
+ return TQColor();
+}
+
+double KSVGIconPainter::dpi()
+{
+ return 90.0; // TODO: make modal?
+}
+
+double KSVGIconPainter::toPixel(const TQString &s, bool hmode)
+{
+ if(s.isEmpty())
+ return 0.0;
+
+ TQString check = s;
+
+ double ret = 0.0;
+
+ double value = 0;
+ const char *start = check.latin1();
+ const char *end = getCoord(start, value);
+
+ if(uint(end - start) < check.length())
+ {
+ if(check.endsWith("px"))
+ ret = value;
+ else if(check.endsWith("cm"))
+ ret = (value / 2.54) * dpi();
+ else if(check.endsWith("pc"))
+ ret = (value / 6.0) * dpi();
+ else if(check.endsWith("mm"))
+ ret = (value / 25.4) * dpi();
+ else if(check.endsWith("in"))
+ ret = value * dpi();
+ else if(check.endsWith("pt"))
+ ret = (value / 72.0) * dpi();
+ else if(check.endsWith("%"))
+ {
+ ret = value / 100.0;
+
+ if(hmode)
+ ret *= d->drawWidth;
+ else
+ ret *= d->drawHeight;
+ }
+ else if(check.endsWith("em"))
+ {
+ ret = value * 10.0; // TODO make this depend on actual font size
+ }
+ }
+ else
+ ret = value;
+
+ return ret;
+}
+
+ArtGradientLinear *KSVGIconPainter::linearGradient(const TQString &id)
+{
+ return d->helper->m_linearGradientMap[id];
+}
+
+void KSVGIconPainter::addLinearGradient(const TQString &id, ArtGradientLinear *gradient)
+{
+ d->helper->m_linearGradientMap.insert(id, gradient);
+}
+
+TQDomElement KSVGIconPainter::linearGradientElement(ArtGradientLinear *linear)
+{
+ return d->helper->m_linearGradientElementMap[linear];
+}
+
+void KSVGIconPainter::addLinearGradientElement(ArtGradientLinear *gradient, TQDomElement element)
+{
+ d->helper->m_linearGradientElementMap.insert(gradient, element);
+}
+
+ArtGradientRadial *KSVGIconPainter::radialGradient(const TQString &id)
+{
+ return d->helper->m_radialGradientMap[id];
+}
+
+void KSVGIconPainter::addRadialGradient(const TQString &id, ArtGradientRadial *gradient)
+{
+ d->helper->m_radialGradientMap.insert(id, gradient);
+}
+
+TQDomElement KSVGIconPainter::radialGradientElement(ArtGradientRadial *radial)
+{
+ return d->helper->m_radialGradientElementMap[radial];
+}
+
+void KSVGIconPainter::addRadialGradientElement(ArtGradientRadial *gradient, TQDomElement element)
+{
+ d->helper->m_radialGradientElementMap.insert(gradient, element);
+}
+
+TQ_UINT32 KSVGIconPainter::toArtColor(const TQColor &color)
+{
+ return d->helper->toArtColor(color);
+}
+
+TQWMatrix KSVGIconPainter::parseTransform(const TQString &transform)
+{
+ TQWMatrix result;
+
+ // Split string for handling 1 transform statement at a time
+ TQStringList subtransforms = TQStringList::split(')', transform);
+ TQStringList::ConstIterator it = subtransforms.begin();
+ TQStringList::ConstIterator end = subtransforms.end();
+ for(; it != end; ++it)
+ {
+ TQStringList subtransform = TQStringList::split('(', (*it));
+
+ subtransform[0] = subtransform[0].stripWhiteSpace().lower();
+ subtransform[1] = subtransform[1].simplifyWhiteSpace();
+ TQRegExp reg("([-]?\\d*\\.?\\d+(?:e[-]?\\d+)?)");
+
+ int pos = 0;
+ TQStringList params;
+
+ while(pos >= 0)
+ {
+ pos = reg.search(subtransform[1], pos);
+ if(pos != -1)
+ {
+ params += reg.cap(1);
+ pos += reg.matchedLength();
+ }
+ }
+
+ if(subtransform[0].startsWith(";") || subtransform[0].startsWith(","))
+ subtransform[0] = subtransform[0].right(subtransform[0].length() - 1);
+
+ if(subtransform[0] == "rotate")
+ {
+ if(params.count() == 3)
+ {
+ double x = params[1].toDouble();
+ double y = params[2].toDouble();
+
+ result.translate(x, y);
+ result.rotate(params[0].toDouble());
+ result.translate(-x, -y);
+ }
+ else
+ result.rotate(params[0].toDouble());
+ }
+ else if(subtransform[0] == "translate")
+ {
+ if(params.count() == 2)
+ result.translate(params[0].toDouble(), params[1].toDouble());
+ else // Spec : if only one param given, assume 2nd param to be 0
+ result.translate(params[0].toDouble() , 0);
+ }
+ else if(subtransform[0] == "scale")
+ {
+ if(params.count() == 2)
+ result.scale(params[0].toDouble(), params[1].toDouble());
+ else // Spec : if only one param given, assume uniform scaling
+ result.scale(params[0].toDouble(), params[0].toDouble());
+ }
+ else if(subtransform[0] == "skewx")
+ result.shear(tan(params[0].toDouble() * deg2rad), 0.0F);
+ else if(subtransform[0] == "skewy")
+ result.shear(tan(params[0].toDouble() * deg2rad), 0.0F);
+ else if(subtransform[0] == "skewy")
+ result.shear(0.0F, tan(params[0].toDouble() * deg2rad));
+ else if(subtransform[0] == "matrix")
+ {
+ if(params.count() >= 6)
+ {
+ result.setMatrix(params[0].toDouble(), params[1].toDouble(), params[2].toDouble(), params[3].toDouble(), params[4].toDouble(), params[5].toDouble());
+ }
+ }
+ }
+
+ return result;
+}
diff --git a/tdecore/svgicons/ksvgiconpainter.h b/tdecore/svgicons/ksvgiconpainter.h
new file mode 100644
index 000000000..6b00d9d7c
--- /dev/null
+++ b/tdecore/svgicons/ksvgiconpainter.h
@@ -0,0 +1,101 @@
+/*
+ Copyright (C) 2002 Nikolas Zimmermann <wildfox@kde.org>
+ This file is part of the KDE project
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ aint with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KSVGIconPainter_H
+#define KSVGIconPainter_H
+
+#include <libart_lgpl/art_render.h>
+#include <libart_lgpl/art_render_gradient.h>
+
+class TQImage;
+class TQColor;
+class TQWMatrix;
+class TQDomElement;
+class TQPointArray;
+
+class KDECORE_EXPORT KSVGIconPainter
+{
+public:
+ KSVGIconPainter(int width, int height);
+ ~KSVGIconPainter();
+
+ void setDrawWidth(int dwidth);
+ void setDrawHeight(int dheight);
+
+ TQImage *image();
+
+ TQWMatrix *tqworldMatrix();
+
+ void finish();
+
+ void setUseFill(bool fill);
+ void setUseStroke(bool stroke);
+
+ void setStrokeWidth(double width);
+ void setStrokeMiterLimit(const TQString &miter);
+ void setCapStyle(const TQString &cap);
+ void setJoinStyle(const TQString &join);
+ void setStrokeColor(const TQString &stroke);
+ void setFillColor(const TQString &fill);
+ void setFillRule(const TQString &fillRule);
+ void setOpacity(const TQString &opacity);
+ void setFillOpacity(const TQString &fillOpacity);
+ void setStrokeOpacity(const TQString &strokeOpacity);
+ void setStrokeDashOffset(const TQString &dashOffset);
+ void setStrokeDashArray(const TQString &dashes);
+
+ void setWorldMatrix(TQWMatrix *tqworldMatrix);
+ void setClippingRect(int x, int y, int w, int h);
+
+ void drawRectangle(double x, double y, double w, double h, double rx, double ry);
+ void drawEllipse(double cx, double cy, double rx, double ry);
+ void drawLine(double x1, double y1, double x2, double y2);
+ void drawPolyline(TQPointArray polyArray, int points = -1);
+ void drawPolygon(TQPointArray polyArray);
+ void drawPath(const TQString &data, bool fill);
+ void drawImage(double x, double y, TQImage &image);
+
+ TQColor parseColor(const TQString &param);
+ TQ_UINT32 toArtColor(const TQColor &color);
+ TQ_UINT32 parseOpacity(const TQString &data);
+
+ double toPixel(const TQString &s, bool hmode);
+ double dpi();
+
+ ArtGradientLinear *linearGradient(const TQString &id);
+ void addLinearGradient(const TQString &id, ArtGradientLinear *gradient);
+
+ TQDomElement linearGradientElement(ArtGradientLinear *linear);
+ void addLinearGradientElement(ArtGradientLinear *gradient, TQDomElement element);
+
+ ArtGradientRadial *radialGradient(const TQString &id);
+ void addRadialGradient(const TQString &id, ArtGradientRadial *gradient);
+
+ TQDomElement radialGradientElement(ArtGradientRadial *radial);
+ void addRadialGradientElement(ArtGradientRadial *gradient, TQDomElement element);
+
+ TQWMatrix parseTransform(const TQString &transform);
+
+private:
+ struct Private;
+ Private *d;
+};
+
+#endif