25 #include <tqwmatrix.h>
32 #include "ksvgiconpainter.h"
33 #include "ksvgiconengine.h"
35 class 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();
206 printf(
"[FIXME] *current = matrix * *current locks up under Qt4; bypassing for now\n");
208 *current = matrix * *current;
212 void parseCommonAttributes(TQDomNode &node)
215 m_engine->painter()->setFillColor(
"black");
216 m_engine->painter()->setStrokeColor(
"none");
217 m_engine->painter()->setStrokeDashArray(
"");
218 m_engine->painter()->setStrokeWidth(1);
219 m_engine->painter()->setJoinStyle(
"");
220 m_engine->painter()->setCapStyle(
"");
225 TQPtrList<TQDomNamedNodeMap> applyList;
226 applyList.setAutoDelete(
true);
228 TQDomNode shape = node.parentNode();
229 for(; !shape.isNull() ; shape = shape.parentNode())
230 applyList.prepend(
new TQDomNamedNodeMap(shape.attributes()));
233 for(TQDomNamedNodeMap *map = applyList.first(); map != 0; map = applyList.next())
235 TQDomNamedNodeMap attr = *map;
237 for(
unsigned int i = 0; i < attr.count(); i++)
239 TQString
name, value;
241 name = attr.item(i).nodeName().lower();
242 value = attr.item(i).nodeValue();
244 if(name ==
"transform")
245 parseTransform(value);
246 else if(name ==
"style")
249 parsePA(name, value);
254 TQDomNamedNodeMap attr = node.attributes();
256 for(
unsigned int i = 0; i < attr.count(); i++)
258 TQDomNode current = attr.item(i);
260 if(current.nodeName().lower() ==
"transform")
261 parseTransform(current.nodeValue());
262 else if(current.nodeName().lower() ==
"style")
263 parseStyle(current.nodeValue());
265 parsePA(current.nodeName().lower(), current.nodeValue());
269 bool handleTags(TQDomElement element,
bool paint)
271 if(element.attribute(
"display") ==
"none")
273 if(element.tagName() ==
"linearGradient")
275 ArtGradientLinear *gradient =
new ArtGradientLinear();
278 gradient->stops = parseGradientStops(element, offsets);
279 gradient->n_stops = offsets + 1;
281 TQString spread = element.attribute(
"spreadMethod");
282 if(spread ==
"repeat")
283 gradient->spread = ART_GRADIENT_REPEAT;
284 else if(spread ==
"reflect")
285 gradient->spread = ART_GRADIENT_REFLECT;
287 gradient->spread = ART_GRADIENT_PAD;
289 m_engine->painter()->addLinearGradient(element.attribute(
"id"), gradient);
290 m_engine->painter()->addLinearGradientElement(gradient, element);
293 else if(element.tagName() ==
"radialGradient")
295 ArtGradientRadial *gradient =
new ArtGradientRadial();
298 gradient->stops = parseGradientStops(element, offsets);
299 gradient->n_stops = offsets + 1;
301 m_engine->painter()->addRadialGradient(element.attribute(
"id"), gradient);
302 m_engine->painter()->addRadialGradientElement(gradient, element);
310 if(element.tagName() ==
"rect")
312 double x = toPixel(element.attribute(
"x"),
true);
313 double y = toPixel(element.attribute(
"y"),
false);
314 double w = toPixel(element.attribute(
"width"),
true);
315 double h = toPixel(element.attribute(
"height"),
false);
320 if(element.hasAttribute(
"rx"))
321 rx = toPixel(element.attribute(
"rx"),
true);
323 if(element.hasAttribute(
"ry"))
324 ry = toPixel(element.attribute(
"ry"),
false);
326 m_engine->painter()->drawRectangle(x, y, w, h, rx, ry);
328 else if(element.tagName() ==
"switch")
330 TQDomNode iterate = element.firstChild();
332 while(!iterate.isNull())
335 m_engine->painter()->setWorldMatrix(
new TQWMatrix(m_initialMatrix));
338 parseCommonAttributes(iterate);
340 if(handleTags(iterate.toElement(),
true))
342 iterate = iterate.nextSibling();
346 else if(element.tagName() ==
"g" || element.tagName() ==
"defs")
348 TQDomNode iterate = element.firstChild();
350 while(!iterate.isNull())
353 m_engine->painter()->setWorldMatrix(
new TQWMatrix(m_initialMatrix));
356 parseCommonAttributes(iterate);
358 handleTags(iterate.toElement(), (element.tagName() ==
"defs") ?
false :
true);
359 iterate = iterate.nextSibling();
363 else if(element.tagName() ==
"line")
365 double x1 = toPixel(element.attribute(
"x1"),
true);
366 double y1 = toPixel(element.attribute(
"y1"),
false);
367 double x2 = toPixel(element.attribute(
"x2"),
true);
368 double y2 = toPixel(element.attribute(
"y2"),
false);
370 m_engine->painter()->drawLine(x1, y1, x2, y2);
373 else if(element.tagName() ==
"circle")
375 double cx = toPixel(element.attribute(
"cx"),
true);
376 double cy = toPixel(element.attribute(
"cy"),
false);
378 double r = toPixel(element.attribute(
"r"),
true);
380 m_engine->painter()->drawEllipse(cx, cy, r, r);
383 else if(element.tagName() ==
"ellipse")
385 double cx = toPixel(element.attribute(
"cx"),
true);
386 double cy = toPixel(element.attribute(
"cy"),
false);
388 double rx = toPixel(element.attribute(
"rx"),
true);
389 double ry = toPixel(element.attribute(
"ry"),
false);
391 m_engine->painter()->drawEllipse(cx, cy, rx, ry);
394 else if(element.tagName() ==
"polyline")
396 TQPointArray polyline = parsePoints(element.attribute(
"points"));
397 m_engine->painter()->drawPolyline(polyline);
400 else if(element.tagName() ==
"polygon")
402 TQPointArray polygon = parsePoints(element.attribute(
"points"));
403 m_engine->painter()->drawPolygon(polygon);
406 else if(element.tagName() ==
"path")
410 if(element.hasAttribute(
"fill") && element.attribute(
"fill").contains(
"none"))
413 if(element.attribute(
"style").contains(
"fill") && element.attribute(
"style").stripWhiteSpace().contains(
"fill:none"))
416 m_engine->painter()->drawPath(element.attribute(
"d"), filled);
419 else if(element.tagName() ==
"image")
421 double x = toPixel(element.attribute(
"x"),
true);
422 double y = toPixel(element.attribute(
"y"),
false);
423 double w = toPixel(element.attribute(
"width"),
true);
424 double h = toPixel(element.attribute(
"height"),
false);
426 TQString href = element.attribute(
"xlink:href");
429 if(href.startsWith(
"data:"))
432 TQCString input = TQString(href.remove(TQRegExp(
"^data:image/.*;base64,"))).utf8();
439 image.loadFromData(output);
447 if(image.width() != (int) w || image.height() != (int) h)
448 image = image.smoothScale((
int) w, (
int) h, TQ_ScaleFree);
450 m_engine->painter()->drawImage(x, y, image);
458 void parseStyle(
const TQString &style)
460 TQStringList substyles = TQStringList::split(
';', style);
461 for(TQStringList::Iterator it = substyles.begin(); it != substyles.end(); ++it)
463 TQStringList substyle = TQStringList::split(
':', (*it));
464 TQString command = substyle[0];
465 TQString params = substyle[1];
466 command = command.stripWhiteSpace();
467 params = params.stripWhiteSpace();
469 parsePA(command, params);
473 void parsePA(
const TQString &command,
const TQString &value)
475 if(command ==
"stroke-width")
476 m_engine->painter()->setStrokeWidth(toPixel(value,
false));
477 else if(command ==
"stroke-miterlimit")
478 m_engine->painter()->setStrokeMiterLimit(value);
479 else if(command ==
"stroke-linecap")
480 m_engine->painter()->setCapStyle(value);
481 else if(command ==
"stroke-linejoin")
482 m_engine->painter()->setJoinStyle(value);
483 else if(command ==
"stroke-dashoffset")
484 m_engine->painter()->setStrokeDashOffset(value);
485 else if(command ==
"stroke-dasharray" && value !=
"none")
486 m_engine->painter()->setStrokeDashArray(value);
487 else if(command ==
"stroke")
488 m_engine->painter()->setStrokeColor(value);
489 else if(command ==
"fill")
490 m_engine->painter()->setFillColor(value);
491 else if(command ==
"fill-rule")
492 m_engine->painter()->setFillRule(value);
493 else if(command ==
"fill-opacity" || command ==
"stroke-opacity" || command ==
"opacity")
495 if(command ==
"fill-opacity")
496 m_engine->painter()->setFillOpacity(value);
497 else if(command ==
"stroke-value")
498 m_engine->painter()->setStrokeOpacity(value);
501 m_engine->painter()->setOpacity(value);
502 m_engine->painter()->setFillOpacity(value);
503 m_engine->painter()->setStrokeOpacity(value);
509 friend class KSVGIconEngine;
511 KSVGIconEngine *m_engine;
512 TQWMatrix m_initialMatrix;
515 struct KSVGIconEngine::Private
517 KSVGIconPainter *painter;
518 KSVGIconEngineHelper *helper;
524 KSVGIconEngine::KSVGIconEngine() : d(new Private())
527 d->helper =
new KSVGIconEngineHelper(
this);
533 KSVGIconEngine::~KSVGIconEngine()
543 bool KSVGIconEngine::load(
int width,
int height,
const TQString &path)
545 if(path.isNull())
return false;
547 TQDomDocument svgDocument(
"svg");
550 if(path.right(3).upper() ==
"SVG")
553 if(!file.open(IO_ReadOnly))
556 svgDocument.setContent(&file);
560 gzFile svgz = gzopen(path.latin1(),
"ro");
567 TQCString buffer(1024);
572 int ret = gzread(svgz, buffer.data() + length, 1024);
578 buffer.resize(buffer.size()+1024);
585 svgDocument.setContent(buffer);
588 if(svgDocument.isNull())
592 TQDomNode rootNode = svgDocument.namedItem(
"svg");
593 if(rootNode.isNull() || !rootNode.isElement())
597 TQDomElement rootElement = rootNode.toElement();
600 d->painter =
new KSVGIconPainter(width, height);
603 if(rootElement.hasAttribute(
"width"))
604 d->width = d->helper->toPixel(rootElement.attribute(
"width"),
true);
607 if(rootElement.hasAttribute(
"height"))
608 d->height = d->helper->toPixel(rootElement.attribute(
"height"),
false);
611 d->painter->setDrawWidth(static_cast<int>(d->width));
612 d->painter->setDrawHeight(static_cast<int>(d->height));
615 d->painter->setClippingRect(0, 0, width, height);
618 if(rootElement.hasAttribute(
"viewBox"))
620 TQStringList points = TQStringList::split(
' ', rootElement.attribute(
"viewBox").simplifyWhiteSpace());
622 float w = points[2].toFloat();
623 float h = points[3].toFloat();
625 double vratiow = width / w;
626 double vratioh = height / h;
631 d->painter->worldMatrix()->scale(vratiow, vratioh);
637 double ratiow = width / d->width;
638 double ratioh = height / d->height;
640 d->painter->worldMatrix()->scale(ratiow, ratioh);
643 TQWMatrix initialMatrix = *d->painter->worldMatrix();
644 d->helper->m_initialMatrix = initialMatrix;
647 if(rootElement.hasAttribute(
"transform"))
648 d->helper->parseTransform(rootElement.attribute(
"transform"));
651 TQDomNode svgNode = rootElement.firstChild();
652 while(!svgNode.isNull())
654 TQDomElement svgChild = svgNode.toElement();
655 if(!svgChild.isNull())
657 d->helper->parseCommonAttributes(svgNode);
658 d->helper->handleTags(svgChild,
true);
661 svgNode = svgNode.nextSibling();
664 d->painter->setWorldMatrix(
new TQWMatrix(initialMatrix));
667 d->painter->finish();
672 KSVGIconPainter *KSVGIconEngine::painter()
677 TQImage *KSVGIconEngine::image()
679 return d->painter->image();
682 double KSVGIconEngine::width()
687 double KSVGIconEngine::height()
static TQCString base64Decode(const TQByteArray &in)
Decodes the given data that was encoded using the base64 algorithm.
const TDEShortcut & end()
Goto end of the document.
const char * name(StdAction id)