32#include "ksvgiconpainter.h"
33#include "ksvgiconengine.h"
35class KSVGIconEngineHelper
38 KSVGIconEngineHelper(KSVGIconEngine *engine)
43 ~KSVGIconEngineHelper()
47 double toPixel(
const TQString &s,
bool hmode)
49 return m_engine->painter()->toPixel(s, hmode);
52 ArtGradientStop *parseGradientStops(TQDomElement element,
int &offsets)
54 if (!element.hasChildNodes())
57 TQValueList<ArtGradientStop> stopList;
59 float oldOffset = -1, newOffset = -1;
60 for(TQDomNode node = element.firstChild(); !node.isNull(); node = node.nextSibling())
62 TQDomElement element = node.toElement();
64 oldOffset = newOffset;
65 TQString temp = element.attribute(
"offset");
67 if(temp.contains(
"%"))
69 temp = temp.left(temp.length() - 1);
70 newOffset = temp.toFloat() / 100.0;
73 newOffset = temp.toFloat();
76 if(oldOffset == newOffset)
80 stopList.append(ArtGradientStop());
82 ArtGradientStop &stop = stopList.last();
84 stop.offset = newOffset;
86 TQString parseOpacity;
89 if(element.hasAttribute(
"stop-opacity"))
90 parseOpacity = element.attribute(
"stop-opacity");
92 if(element.hasAttribute(
"stop-color"))
93 parseColor = element.attribute(
"stop-color");
95 if(parseOpacity.isEmpty() || parseColor.isEmpty())
97 TQString style = element.attribute(
"style");
99 TQStringList substyles = TQStringList::split(
';', style);
100 for(TQStringList::Iterator it = substyles.begin(); it != substyles.end(); ++it)
102 TQStringList substyle = TQStringList::split(
':', (*it));
103 TQString command = substyle[0];
104 TQString params = substyle[1];
105 command = command.stripWhiteSpace();
106 params = params.stripWhiteSpace();
108 if(command ==
"stop-color")
112 if(!parseOpacity.isEmpty())
115 else if(command ==
"stop-opacity")
117 parseOpacity = params;
119 if(!parseColor.isEmpty())
127 TQColor qStopColor = m_engine->painter()->parseColor(parseColor);
130 TQ_UINT32 stopColor = m_engine->painter()->toArtColor(qStopColor);
132 int opacity = m_engine->painter()->parseOpacity(parseOpacity);
134 TQ_UINT32 rgba = (stopColor << 8) | opacity;
135 TQ_UINT32 r, g, b, a;
139 r = (rgba >> 24) * a + 0x80;
140 r = (r + (r >> 8)) >> 8;
141 g = ((rgba >> 16) & 0xff) * a + 0x80;
142 g = (g + (g >> 8)) >> 8;
143 b = ((rgba >> 8) & 0xff) * a + 0x80;
144 b = (b + (b >> 8)) >> 8;
146 stop.color[0] = ART_PIX_MAX_FROM_8(r);
147 stop.color[1] = ART_PIX_MAX_FROM_8(g);
148 stop.color[2] = ART_PIX_MAX_FROM_8(b);
149 stop.color[3] = ART_PIX_MAX_FROM_8(a);
152 if (stopList.isEmpty())
155 ArtGradientStop *stops =
new ArtGradientStop[stopList.count()];
157 TQValueList<ArtGradientStop>::iterator it = stopList.begin();
158 TQValueList<ArtGradientStop>::iterator
end = stopList.end();
160 for (
int i = 0; it !=
end; ++i, ++it)
166 TQPointArray parsePoints(TQString points)
169 return TQPointArray();
171 points = points.simplifyWhiteSpace();
173 if(points.contains(
",,") || points.contains(
", ,"))
174 return TQPointArray();
176 points.replace(
',',
' ');
177 points.replace(
'\r', TQString());
178 points.replace(
'\n', TQString());
180 points = points.simplifyWhiteSpace();
182 TQStringList pointList = TQStringList::split(
' ', points);
184 TQPointArray array(pointList.count() / 2);
187 for(TQStringList::Iterator it = pointList.begin(); it != pointList.end(); it++)
189 float x = (*(it++)).toFloat();
190 float y = (*(it)).toFloat();
192 array.setPoint(i,
static_cast<int>(x),
static_cast<int>(y));
199 void parseTransform(
const TQString &transform)
202 TQWMatrix matrix = m_engine->painter()->parseTransform(transform);
204 TQWMatrix *current = m_engine->painter()->worldMatrix();
205 *current = matrix * *current;
208 void parseCommonAttributes(TQDomNode &node)
211 m_engine->painter()->setFillColor(
"black");
212 m_engine->painter()->setStrokeColor(
"none");
213 m_engine->painter()->setStrokeDashArray(
"");
214 m_engine->painter()->setStrokeWidth(1);
215 m_engine->painter()->setJoinStyle(
"");
216 m_engine->painter()->setCapStyle(
"");
221 TQPtrList<TQDomNamedNodeMap> applyList;
222 applyList.setAutoDelete(
true);
224 TQDomNode shape = node.parentNode();
225 for(; !shape.isNull() ; shape = shape.parentNode())
226 applyList.prepend(
new TQDomNamedNodeMap(shape.attributes()));
229 for(TQDomNamedNodeMap *map = applyList.first(); map != 0; map = applyList.next())
231 TQDomNamedNodeMap attr = *map;
233 for(
unsigned int i = 0; i < attr.count(); i++)
235 TQString
name, value;
237 name = attr.item(i).nodeName().lower();
238 value = attr.item(i).nodeValue();
240 if(name ==
"transform")
241 parseTransform(value);
242 else if(name ==
"style")
245 parsePA(name, value);
250 TQDomNamedNodeMap attr = node.attributes();
252 for(
unsigned int i = 0; i < attr.count(); i++)
254 TQDomNode current = attr.item(i);
256 if(current.nodeName().lower() ==
"transform")
257 parseTransform(current.nodeValue());
258 else if(current.nodeName().lower() ==
"style")
259 parseStyle(current.nodeValue());
261 parsePA(current.nodeName().lower(), current.nodeValue());
265 bool handleTags(TQDomElement element,
bool paint)
267 if(element.attribute(
"display") ==
"none")
269 if(element.tagName() ==
"linearGradient")
271 ArtGradientLinear *gradient =
new ArtGradientLinear();
274 gradient->stops = parseGradientStops(element, offsets);
275 gradient->n_stops = offsets + 1;
277 TQString spread = element.attribute(
"spreadMethod");
278 if(spread ==
"repeat")
279 gradient->spread = ART_GRADIENT_REPEAT;
280 else if(spread ==
"reflect")
281 gradient->spread = ART_GRADIENT_REFLECT;
283 gradient->spread = ART_GRADIENT_PAD;
285 m_engine->painter()->addLinearGradient(element.attribute(
"id"), gradient);
286 m_engine->painter()->addLinearGradientElement(gradient, element);
289 else if(element.tagName() ==
"radialGradient")
291 ArtGradientRadial *gradient =
new ArtGradientRadial();
294 gradient->stops = parseGradientStops(element, offsets);
295 gradient->n_stops = offsets + 1;
297 m_engine->painter()->addRadialGradient(element.attribute(
"id"), gradient);
298 m_engine->painter()->addRadialGradientElement(gradient, element);
306 if(element.tagName() ==
"rect")
308 double x = toPixel(element.attribute(
"x"),
true);
309 double y = toPixel(element.attribute(
"y"),
false);
310 double w = toPixel(element.attribute(
"width"),
true);
311 double h = toPixel(element.attribute(
"height"),
false);
316 if(element.hasAttribute(
"rx"))
317 rx = toPixel(element.attribute(
"rx"),
true);
319 if(element.hasAttribute(
"ry"))
320 ry = toPixel(element.attribute(
"ry"),
false);
322 m_engine->painter()->drawRectangle(x, y, w, h, rx, ry);
324 else if(element.tagName() ==
"switch")
326 TQDomNode iterate = element.firstChild();
328 while(!iterate.isNull())
331 m_engine->painter()->setWorldMatrix(
new TQWMatrix(m_initialMatrix));
334 parseCommonAttributes(iterate);
336 if(handleTags(iterate.toElement(),
true))
338 iterate = iterate.nextSibling();
342 else if(element.tagName() ==
"g" || element.tagName() ==
"defs")
344 TQDomNode iterate = element.firstChild();
346 while(!iterate.isNull())
349 m_engine->painter()->setWorldMatrix(
new TQWMatrix(m_initialMatrix));
352 parseCommonAttributes(iterate);
354 handleTags(iterate.toElement(), (element.tagName() ==
"defs") ?
false :
true);
355 iterate = iterate.nextSibling();
359 else if(element.tagName() ==
"line")
361 double x1 = toPixel(element.attribute(
"x1"),
true);
362 double y1 = toPixel(element.attribute(
"y1"),
false);
363 double x2 = toPixel(element.attribute(
"x2"),
true);
364 double y2 = toPixel(element.attribute(
"y2"),
false);
366 m_engine->painter()->drawLine(x1, y1, x2, y2);
369 else if(element.tagName() ==
"circle")
371 double cx = toPixel(element.attribute(
"cx"),
true);
372 double cy = toPixel(element.attribute(
"cy"),
false);
374 double r = toPixel(element.attribute(
"r"),
true);
376 m_engine->painter()->drawEllipse(cx, cy, r, r);
379 else if(element.tagName() ==
"ellipse")
381 double cx = toPixel(element.attribute(
"cx"),
true);
382 double cy = toPixel(element.attribute(
"cy"),
false);
384 double rx = toPixel(element.attribute(
"rx"),
true);
385 double ry = toPixel(element.attribute(
"ry"),
false);
387 m_engine->painter()->drawEllipse(cx, cy, rx, ry);
390 else if(element.tagName() ==
"polyline")
392 TQPointArray polyline = parsePoints(element.attribute(
"points"));
393 m_engine->painter()->drawPolyline(polyline);
396 else if(element.tagName() ==
"polygon")
398 TQPointArray polygon = parsePoints(element.attribute(
"points"));
399 m_engine->painter()->drawPolygon(polygon);
402 else if(element.tagName() ==
"path")
406 if(element.hasAttribute(
"fill") && element.attribute(
"fill").contains(
"none"))
409 if(element.attribute(
"style").contains(
"fill") && element.attribute(
"style").stripWhiteSpace().contains(
"fill:none"))
412 m_engine->painter()->drawPath(element.attribute(
"d"), filled);
415 else if(element.tagName() ==
"image")
417 double x = toPixel(element.attribute(
"x"),
true);
418 double y = toPixel(element.attribute(
"y"),
false);
419 double w = toPixel(element.attribute(
"width"),
true);
420 double h = toPixel(element.attribute(
"height"),
false);
422 TQString href = element.attribute(
"xlink:href");
425 if(href.startsWith(
"data:"))
428 TQCString input = TQString(href.remove(TQRegExp(
"^data:image/.*;base64,"))).utf8();
435 image.loadFromData(output);
443 if(image.width() != (
int) w || image.height() != (
int) h)
444 image = image.smoothScale((
int) w, (
int) h, TQImage::ScaleFree);
446 m_engine->painter()->drawImage(x, y, image);
454 void parseStyle(
const TQString &style)
456 TQStringList substyles = TQStringList::split(
';', style);
457 for(TQStringList::Iterator it = substyles.begin(); it != substyles.end(); ++it)
459 TQStringList substyle = TQStringList::split(
':', (*it));
460 TQString command = substyle[0];
461 TQString params = substyle[1];
462 command = command.stripWhiteSpace();
463 params = params.stripWhiteSpace();
465 parsePA(command, params);
469 void parsePA(
const TQString &command,
const TQString &value)
471 if(command ==
"stroke-width")
472 m_engine->painter()->setStrokeWidth(toPixel(value,
false));
473 else if(command ==
"stroke-miterlimit")
474 m_engine->painter()->setStrokeMiterLimit(value);
475 else if(command ==
"stroke-linecap")
476 m_engine->painter()->setCapStyle(value);
477 else if(command ==
"stroke-linejoin")
478 m_engine->painter()->setJoinStyle(value);
479 else if(command ==
"stroke-dashoffset")
480 m_engine->painter()->setStrokeDashOffset(value);
481 else if(command ==
"stroke-dasharray" && value !=
"none")
482 m_engine->painter()->setStrokeDashArray(value);
483 else if(command ==
"stroke")
484 m_engine->painter()->setStrokeColor(value);
485 else if(command ==
"fill")
486 m_engine->painter()->setFillColor(value);
487 else if(command ==
"fill-rule")
488 m_engine->painter()->setFillRule(value);
489 else if(command ==
"fill-opacity" || command ==
"stroke-opacity" || command ==
"opacity")
491 if(command ==
"fill-opacity")
492 m_engine->painter()->setFillOpacity(value);
493 else if(command ==
"stroke-value")
494 m_engine->painter()->setStrokeOpacity(value);
497 m_engine->painter()->setOpacity(value);
498 m_engine->painter()->setFillOpacity(value);
499 m_engine->painter()->setStrokeOpacity(value);
505 friend class KSVGIconEngine;
507 KSVGIconEngine *m_engine;
508 TQWMatrix m_initialMatrix;
511struct KSVGIconEngine::Private
513 KSVGIconPainter *painter;
514 KSVGIconEngineHelper *helper;
520KSVGIconEngine::KSVGIconEngine() : d(new Private())
523 d->helper =
new KSVGIconEngineHelper(
this);
529KSVGIconEngine::~KSVGIconEngine()
539bool KSVGIconEngine::load(
int width,
int height,
const TQString &path)
541 if(path.isNull())
return false;
543 TQDomDocument svgDocument(
"svg");
546 if(path.right(3).upper() ==
"SVG")
549 if(!file.open(IO_ReadOnly))
552 svgDocument.setContent(&file);
556 gzFile svgz = gzopen(path.latin1(),
"ro");
563 TQCString buffer(1024);
568 int ret = gzread(svgz, buffer.data() + length, 1024);
574 buffer.resize(buffer.size()+1024);
581 svgDocument.setContent(buffer);
584 if(svgDocument.isNull())
588 TQDomNode rootNode = svgDocument.namedItem(
"svg");
589 if(rootNode.isNull() || !rootNode.isElement())
593 TQDomElement rootElement = rootNode.toElement();
596 d->painter =
new KSVGIconPainter(width, height);
599 if(rootElement.hasAttribute(
"width"))
600 d->width = d->helper->toPixel(rootElement.attribute(
"width"),
true);
603 if(rootElement.hasAttribute(
"height"))
604 d->height = d->helper->toPixel(rootElement.attribute(
"height"),
false);
607 d->painter->setDrawWidth(
static_cast<int>(d->width));
608 d->painter->setDrawHeight(
static_cast<int>(d->height));
611 d->painter->setClippingRect(0, 0, width, height);
614 if(rootElement.hasAttribute(
"viewBox"))
616 TQStringList points = TQStringList::split(
' ', rootElement.attribute(
"viewBox").simplifyWhiteSpace());
618 float w = points[2].toFloat();
619 float h = points[3].toFloat();
621 double vratiow = width / w;
622 double vratioh = height / h;
627 d->painter->worldMatrix()->scale(vratiow, vratioh);
633 double ratiow = width / d->width;
634 double ratioh = height / d->height;
636 d->painter->worldMatrix()->scale(ratiow, ratioh);
639 TQWMatrix initialMatrix = *d->painter->worldMatrix();
640 d->helper->m_initialMatrix = initialMatrix;
643 if(rootElement.hasAttribute(
"transform"))
644 d->helper->parseTransform(rootElement.attribute(
"transform"));
647 TQDomNode svgNode = rootElement.firstChild();
648 while(!svgNode.isNull())
650 TQDomElement svgChild = svgNode.toElement();
651 if(!svgChild.isNull())
653 d->helper->parseCommonAttributes(svgNode);
654 d->helper->handleTags(svgChild,
true);
657 svgNode = svgNode.nextSibling();
660 d->painter->setWorldMatrix(
new TQWMatrix(initialMatrix));
663 d->painter->finish();
668KSVGIconPainter *KSVGIconEngine::painter()
673TQImage *KSVGIconEngine::image()
675 return d->painter->image();
678double KSVGIconEngine::width()
683double KSVGIconEngine::height()
static TQCString base64Decode(const TQByteArray &in)
Decodes the given data that was encoded using the base64 algorithm.
const char * name(StdAction id)
const TDEShortcut & end()
Goto end of the document.