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 TQDomElement element2 = iterate.toElement();
356 handleTags(element2, (element.tagName() ==
"defs") ?
false : true);
358 if(element2.attribute(
"type") ==
"text/css") {
360 TQString el2text = element2.text().remove(
" ").remove(
'"').remove(
"'").remove(
'\r').remove(
'\n');
362 if(! el2text.isEmpty()) {
363 flg_svg_has_css_colors =
true;
364 cssColorSchemeText = el2text.section(
".ColorScheme-Text{color:", 1).section(
";", 0, 0);
365 cssColorSchemeHighlight = el2text.section(
".ColorScheme-Highlight{color:", 1).section(
";", 0, 0);
366 cssColorSchemeNeutralText = el2text.section(
".ColorScheme-NeutralText{color:", 1).section(
";", 0, 0);
367 cssColorSchemePositiveText = el2text.section(
".ColorScheme-PositiveText{color:", 1).section(
";", 0, 0);
368 cssColorSchemeNegativeText = el2text.section(
".ColorScheme-NegativeText{color:", 1).section(
";", 0, 0);
377 iterate = iterate.nextSibling();
381 else if(element.tagName() ==
"line")
383 double x1 = toPixel(element.attribute(
"x1"),
true);
384 double y1 = toPixel(element.attribute(
"y1"),
false);
385 double x2 = toPixel(element.attribute(
"x2"),
true);
386 double y2 = toPixel(element.attribute(
"y2"),
false);
388 m_engine->painter()->drawLine(x1, y1, x2, y2);
391 else if(element.tagName() ==
"circle")
393 double cx = toPixel(element.attribute(
"cx"),
true);
394 double cy = toPixel(element.attribute(
"cy"),
false);
396 double r = toPixel(element.attribute(
"r"),
true);
398 m_engine->painter()->drawEllipse(cx, cy, r, r);
401 else if(element.tagName() ==
"ellipse")
403 double cx = toPixel(element.attribute(
"cx"),
true);
404 double cy = toPixel(element.attribute(
"cy"),
false);
406 double rx = toPixel(element.attribute(
"rx"),
true);
407 double ry = toPixel(element.attribute(
"ry"),
false);
409 m_engine->painter()->drawEllipse(cx, cy, rx, ry);
412 else if(element.tagName() ==
"polyline")
414 TQPointArray polyline = parsePoints(element.attribute(
"points"));
415 m_engine->painter()->drawPolyline(polyline);
418 else if(element.tagName() ==
"polygon")
420 TQPointArray polygon = parsePoints(element.attribute(
"points"));
421 m_engine->painter()->drawPolygon(polygon);
424 else if(element.tagName() ==
"path")
428 if(element.hasAttribute(
"fill") && element.attribute(
"fill").contains(
"none"))
431 if(element.attribute(
"style").contains(
"fill") && element.attribute(
"style").stripWhiteSpace().contains(
"fill:none"))
434 if(filled && (flg_apply_css_fill_color || flg_apply_css_stroke_color)) {
436 TQString attr1 = element.attribute(
"class");
438 TQString hlp1_csscolor;
439 if(attr1.isEmpty()) {
440 hlp1_csscolor =
"#888888";
442 }
else if(attr1 ==
"ColorScheme-Text") {
443 hlp1_csscolor = cssColorSchemeText;
444 }
else if(attr1 ==
"ColorScheme-Highlight") {
445 hlp1_csscolor = cssColorSchemeHighlight;
446 }
else if(attr1 ==
"ColorScheme-NeutralText") {
447 hlp1_csscolor = cssColorSchemeNeutralText;
448 }
else if(attr1 ==
"ColorScheme-PositiveText") {
449 hlp1_csscolor = cssColorSchemePositiveText;
450 }
else if(attr1 ==
"ColorScheme-NegativeText") {
451 hlp1_csscolor = cssColorSchemeNegativeText;
453 hlp1_csscolor =
"#888888";
457 if(flg_apply_css_fill_color) {
459 m_engine->painter()->setFillColor(hlp1_csscolor);
460 flg_apply_css_fill_color =
false;
461 }
else if(flg_apply_css_stroke_color) {
463 m_engine->painter()->setStrokeColor(hlp1_csscolor);
464 flg_apply_css_stroke_color =
false;
468 m_engine->painter()->drawPath(element.attribute(
"d"), filled);
471 else if(element.tagName() ==
"image")
473 double x = toPixel(element.attribute(
"x"),
true);
474 double y = toPixel(element.attribute(
"y"),
false);
475 double w = toPixel(element.attribute(
"width"),
true);
476 double h = toPixel(element.attribute(
"height"),
false);
478 TQString href = element.attribute(
"xlink:href");
481 if(href.startsWith(
"data:"))
484 TQCString input = TQString(href.remove(TQRegExp(
"^data:image/.*;base64,"))).utf8();
491 image.loadFromData(output);
499 if(image.width() != (
int) w || image.height() != (
int) h)
500 image = image.smoothScale((
int) w, (
int) h, TQImage::ScaleFree);
502 m_engine->painter()->drawImage(x, y, image);
510 void parseStyle(
const TQString &style)
512 TQStringList substyles = TQStringList::split(
';', style);
513 for(TQStringList::Iterator it = substyles.begin(); it != substyles.end(); ++it)
515 TQStringList substyle = TQStringList::split(
':', (*it));
516 TQString command = substyle[0];
517 TQString params = substyle[1];
518 command = command.stripWhiteSpace();
519 params = params.stripWhiteSpace();
526 parsePA(command, params);
530 void parsePA(
const TQString &command,
const TQString &value)
532 if(command ==
"stroke-width")
533 m_engine->painter()->setStrokeWidth(toPixel(value,
false));
534 else if(command ==
"stroke-miterlimit")
535 m_engine->painter()->setStrokeMiterLimit(value);
536 else if(command ==
"stroke-linecap")
537 m_engine->painter()->setCapStyle(value);
538 else if(command ==
"stroke-linejoin")
539 m_engine->painter()->setJoinStyle(value);
540 else if(command ==
"stroke-dashoffset")
541 m_engine->painter()->setStrokeDashOffset(value);
542 else if(command ==
"stroke-dasharray" && value !=
"none")
543 m_engine->painter()->setStrokeDashArray(value);
544 else if(command ==
"stroke")
546 if(flg_svg_has_css_colors && (value ==
"currentColor")) {
549 flg_apply_css_stroke_color =
true;
551 m_engine->painter()->setStrokeColor(value);
554 else if(command ==
"fill")
556 if(flg_svg_has_css_colors && (value ==
"currentColor")) {
559 flg_apply_css_fill_color =
true;
561 m_engine->painter()->setFillColor(value);
564 else if(command ==
"fill-rule")
565 m_engine->painter()->setFillRule(value);
566 else if(command ==
"fill-opacity" || command ==
"stroke-opacity" || command ==
"opacity")
568 if(command ==
"fill-opacity")
569 m_engine->painter()->setFillOpacity(value);
570 else if(command ==
"stroke-value")
571 m_engine->painter()->setStrokeOpacity(value);
574 m_engine->painter()->setOpacity(value);
575 m_engine->painter()->setFillOpacity(value);
576 m_engine->painter()->setStrokeOpacity(value);
582 friend class KSVGIconEngine;
584 KSVGIconEngine *m_engine;
585 TQWMatrix m_initialMatrix;
587 TQString cssColorSchemeText;
588 TQString cssColorSchemeHighlight;
589 TQString cssColorSchemeNeutralText;
590 TQString cssColorSchemePositiveText;
591 TQString cssColorSchemeNegativeText;
592 bool flg_svg_has_css_colors =
false;
593 bool flg_apply_css_fill_color =
false;
594 bool flg_apply_css_stroke_color =
false;
597struct KSVGIconEngine::Private
599 KSVGIconPainter *painter;
600 KSVGIconEngineHelper *helper;
606KSVGIconEngine::KSVGIconEngine() : d(new Private())
609 d->helper =
new KSVGIconEngineHelper(
this);
615KSVGIconEngine::~KSVGIconEngine()
625bool KSVGIconEngine::load(
int width,
int height,
const TQString &path)
627 if(path.isNull())
return false;
631 TQDomDocument svgDocument(
"svg");
634 if(path.right(3).upper() ==
"SVG")
637 if(!file.open(IO_ReadOnly))
640 svgDocument.setContent(&file);
644 gzFile svgz = gzopen(path.latin1(),
"ro");
651 TQCString buffer(1024);
656 int ret = gzread(svgz, buffer.data() + length, 1024);
662 buffer.resize(buffer.size()+1024);
669 svgDocument.setContent(buffer);
672 if(svgDocument.isNull())
676 TQDomNode rootNode = svgDocument.namedItem(
"svg");
677 if(rootNode.isNull() || !rootNode.isElement())
681 TQDomElement rootElement = rootNode.toElement();
684 d->painter =
new KSVGIconPainter(width, height);
687 if(rootElement.hasAttribute(
"width"))
688 d->width = d->helper->toPixel(rootElement.attribute(
"width"),
true);
691 if(rootElement.hasAttribute(
"height"))
692 d->height = d->helper->toPixel(rootElement.attribute(
"height"),
false);
695 d->painter->setDrawWidth(
static_cast<int>(d->width));
696 d->painter->setDrawHeight(
static_cast<int>(d->height));
699 d->painter->setClippingRect(0, 0, width, height);
702 if(rootElement.hasAttribute(
"viewBox"))
704 TQStringList points = TQStringList::split(
' ', rootElement.attribute(
"viewBox").simplifyWhiteSpace());
706 float w = points[2].toFloat();
707 float h = points[3].toFloat();
709 double vratiow = width / w;
710 double vratioh = height / h;
715 d->painter->worldMatrix()->scale(vratiow, vratioh);
721 double ratiow = width / d->width;
722 double ratioh = height / d->height;
724 d->painter->worldMatrix()->scale(ratiow, ratioh);
727 TQWMatrix initialMatrix = *d->painter->worldMatrix();
728 d->helper->m_initialMatrix = initialMatrix;
731 if(rootElement.hasAttribute(
"transform"))
732 d->helper->parseTransform(rootElement.attribute(
"transform"));
735 TQDomNode svgNode = rootElement.firstChild();
736 while(!svgNode.isNull())
738 TQDomElement svgChild = svgNode.toElement();
739 if(!svgChild.isNull())
741 d->helper->parseCommonAttributes(svgNode);
742 d->helper->handleTags(svgChild,
true);
745 svgNode = svgNode.nextSibling();
748 d->painter->setWorldMatrix(
new TQWMatrix(initialMatrix));
751 d->painter->finish();
756KSVGIconPainter *KSVGIconEngine::painter()
761TQImage *KSVGIconEngine::image()
763 return d->painter->image();
766double KSVGIconEngine::width()
771double 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.