00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include <tqdom.h>
00022 #include <tqfile.h>
00023 #include <tqcolor.h>
00024 #include <tqimage.h>
00025 #include <tqwmatrix.h>
00026 #include <tqregexp.h>
00027
00028 #include <kmdcodec.h>
00029
00030 #include <zlib.h>
00031
00032 #include "ksvgiconpainter.h"
00033 #include "ksvgiconengine.h"
00034
00035 class KSVGIconEngineHelper
00036 {
00037 public:
00038 KSVGIconEngineHelper(KSVGIconEngine *engine)
00039 {
00040 m_engine = engine;
00041 }
00042
00043 ~KSVGIconEngineHelper()
00044 {
00045 }
00046
00047 double toPixel(const TQString &s, bool hmode)
00048 {
00049 return m_engine->painter()->toPixel(s, hmode);
00050 }
00051
00052 ArtGradientStop *parseGradientStops(TQDomElement element, int &offsets)
00053 {
00054 if (!element.hasChildNodes())
00055 return 0;
00056
00057 TQValueList<ArtGradientStop> stopList;
00058
00059 float oldOffset = -1, newOffset = -1;
00060 for(TQDomNode node = element.firstChild(); !node.isNull(); node = node.nextSibling())
00061 {
00062 TQDomElement element = node.toElement();
00063
00064 oldOffset = newOffset;
00065 TQString temp = element.attribute("offset");
00066
00067 if(temp.contains("%"))
00068 {
00069 temp = temp.left(temp.length() - 1);
00070 newOffset = temp.toFloat() / 100.0;
00071 }
00072 else
00073 newOffset = temp.toFloat();
00074
00075
00076 if(oldOffset == newOffset)
00077 continue;
00078
00079 offsets++;
00080 stopList.append(ArtGradientStop());
00081
00082 ArtGradientStop &stop = stopList.last();
00083
00084 stop.offset = newOffset;
00085
00086 TQString parseOpacity;
00087 TQString parseColor;
00088
00089 if(element.hasAttribute("stop-opacity"))
00090 parseOpacity = element.attribute("stop-opacity");
00091
00092 if(element.hasAttribute("stop-color"))
00093 parseColor = element.attribute("stop-color");
00094
00095 if(parseOpacity.isEmpty() || parseColor.isEmpty())
00096 {
00097 TQString style = element.attribute("style");
00098
00099 TQStringList substyles = TQStringList::split(';', style);
00100 for(TQStringList::Iterator it = substyles.begin(); it != substyles.end(); ++it)
00101 {
00102 TQStringList substyle = TQStringList::split(':', (*it));
00103 TQString command = substyle[0];
00104 TQString params = substyle[1];
00105 command = command.stripWhiteSpace();
00106 params = params.stripWhiteSpace();
00107
00108 if(command == "stop-color")
00109 {
00110 parseColor = params;
00111
00112 if(!parseOpacity.isEmpty())
00113 break;
00114 }
00115 else if(command == "stop-opacity")
00116 {
00117 parseOpacity = params;
00118
00119 if(!parseColor.isEmpty())
00120 break;
00121 }
00122 }
00123 }
00124
00125
00126
00127 TQColor qStopColor = m_engine->painter()->parseColor(parseColor);
00128
00129
00130 TQ_UINT32 stopColor = m_engine->painter()->toArtColor(qStopColor);
00131
00132 int opacity = m_engine->painter()->parseOpacity(parseOpacity);
00133
00134 TQ_UINT32 rgba = (stopColor << 8) | opacity;
00135 TQ_UINT32 r, g, b, a;
00136
00137
00138 a = rgba & 0xff;
00139 r = (rgba >> 24) * a + 0x80;
00140 r = (r + (r >> 8)) >> 8;
00141 g = ((rgba >> 16) & 0xff) * a + 0x80;
00142 g = (g + (g >> 8)) >> 8;
00143 b = ((rgba >> 8) & 0xff) * a + 0x80;
00144 b = (b + (b >> 8)) >> 8;
00145
00146 stop.color[0] = ART_PIX_MAX_FROM_8(r);
00147 stop.color[1] = ART_PIX_MAX_FROM_8(g);
00148 stop.color[2] = ART_PIX_MAX_FROM_8(b);
00149 stop.color[3] = ART_PIX_MAX_FROM_8(a);
00150 }
00151
00152 if (stopList.isEmpty())
00153 return 0;
00154
00155 ArtGradientStop *stops = new ArtGradientStop[stopList.count()];
00156
00157 TQValueList<ArtGradientStop>::iterator it = stopList.begin();
00158 TQValueList<ArtGradientStop>::iterator end = stopList.end();
00159
00160 for (int i = 0; it != end; ++i, ++it)
00161 stops[i] = *it;
00162
00163 return stops;
00164 }
00165
00166 TQPointArray parsePoints(TQString points)
00167 {
00168 if(points.isEmpty())
00169 return TQPointArray();
00170
00171 points = points.simplifyWhiteSpace();
00172
00173 if(points.contains(",,") || points.contains(", ,"))
00174 return TQPointArray();
00175
00176 points.replace(',', ' ');
00177 points.replace('\r', TQString());
00178 points.replace('\n', TQString());
00179
00180 points = points.simplifyWhiteSpace();
00181
00182 TQStringList pointList = TQStringList::split(' ', points);
00183
00184 TQPointArray array(pointList.count() / 2);
00185 int i = 0;
00186
00187 for(TQStringList::Iterator it = pointList.begin(); it != pointList.end(); it++)
00188 {
00189 float x = (*(it++)).toFloat();
00190 float y = (*(it)).toFloat();
00191
00192 array.setPoint(i, static_cast<int>(x), static_cast<int>(y));
00193 i++;
00194 }
00195
00196 return array;
00197 }
00198
00199 void parseTransform(const TQString &transform)
00200 {
00201
00202 TQWMatrix matrix = m_engine->painter()->parseTransform(transform);
00203
00204 TQWMatrix *current = m_engine->painter()->worldMatrix();
00205 #ifdef USE_QT4
00206 printf("[FIXME] *current = matrix * *current locks up under Qt4; bypassing for now\n");
00207 #else // USE_QT4
00208 *current = matrix * *current;
00209 #endif // USE_QT4
00210 }
00211
00212 void parseCommonAttributes(TQDomNode &node)
00213 {
00214
00215 m_engine->painter()->setFillColor("black");
00216 m_engine->painter()->setStrokeColor("none");
00217 m_engine->painter()->setStrokeDashArray("");
00218 m_engine->painter()->setStrokeWidth(1);
00219 m_engine->painter()->setJoinStyle("");
00220 m_engine->painter()->setCapStyle("");
00221
00222
00223
00224
00225 TQPtrList<TQDomNamedNodeMap> applyList;
00226 applyList.setAutoDelete(true);
00227
00228 TQDomNode shape = node.parentNode();
00229 for(; !shape.isNull() ; shape = shape.parentNode())
00230 applyList.prepend(new TQDomNamedNodeMap(shape.attributes()));
00231
00232
00233 for(TQDomNamedNodeMap *map = applyList.first(); map != 0; map = applyList.next())
00234 {
00235 TQDomNamedNodeMap attr = *map;
00236
00237 for(unsigned int i = 0; i < attr.count(); i++)
00238 {
00239 TQString name, value;
00240
00241 name = attr.item(i).nodeName().lower();
00242 value = attr.item(i).nodeValue();
00243
00244 if(name == "transform")
00245 parseTransform(value);
00246 else if(name == "style")
00247 parseStyle(value);
00248 else
00249 parsePA(name, value);
00250 }
00251 }
00252
00253
00254 TQDomNamedNodeMap attr = node.attributes();
00255
00256 for(unsigned int i = 0; i < attr.count(); i++)
00257 {
00258 TQDomNode current = attr.item(i);
00259
00260 if(current.nodeName().lower() == "transform")
00261 parseTransform(current.nodeValue());
00262 else if(current.nodeName().lower() == "style")
00263 parseStyle(current.nodeValue());
00264 else
00265 parsePA(current.nodeName().lower(), current.nodeValue());
00266 }
00267 }
00268
00269 bool handleTags(TQDomElement element, bool paint)
00270 {
00271 if(element.attribute("display") == "none")
00272 return false;
00273 if(element.tagName() == "linearGradient")
00274 {
00275 ArtGradientLinear *gradient = new ArtGradientLinear();
00276
00277 int offsets = -1;
00278 gradient->stops = parseGradientStops(element, offsets);
00279 gradient->n_stops = offsets + 1;
00280
00281 TQString spread = element.attribute("spreadMethod");
00282 if(spread == "repeat")
00283 gradient->spread = ART_GRADIENT_REPEAT;
00284 else if(spread == "reflect")
00285 gradient->spread = ART_GRADIENT_REFLECT;
00286 else
00287 gradient->spread = ART_GRADIENT_PAD;
00288
00289 m_engine->painter()->addLinearGradient(element.attribute("id"), gradient);
00290 m_engine->painter()->addLinearGradientElement(gradient, element);
00291 return true;
00292 }
00293 else if(element.tagName() == "radialGradient")
00294 {
00295 ArtGradientRadial *gradient = new ArtGradientRadial();
00296
00297 int offsets = -1;
00298 gradient->stops = parseGradientStops(element, offsets);
00299 gradient->n_stops = offsets + 1;
00300
00301 m_engine->painter()->addRadialGradient(element.attribute("id"), gradient);
00302 m_engine->painter()->addRadialGradientElement(gradient, element);
00303 return true;
00304 }
00305
00306 if(!paint)
00307 return true;
00308
00309
00310 if(element.tagName() == "rect")
00311 {
00312 double x = toPixel(element.attribute("x"), true);
00313 double y = toPixel(element.attribute("y"), false);
00314 double w = toPixel(element.attribute("width"), true);
00315 double h = toPixel(element.attribute("height"), false);
00316
00317 double rx = 0.0;
00318 double ry = 0.0;
00319
00320 if(element.hasAttribute("rx"))
00321 rx = toPixel(element.attribute("rx"), true);
00322
00323 if(element.hasAttribute("ry"))
00324 ry = toPixel(element.attribute("ry"), false);
00325
00326 m_engine->painter()->drawRectangle(x, y, w, h, rx, ry);
00327 }
00328 else if(element.tagName() == "switch")
00329 {
00330 TQDomNode iterate = element.firstChild();
00331
00332 while(!iterate.isNull())
00333 {
00334
00335 m_engine->painter()->setWorldMatrix(new TQWMatrix(m_initialMatrix));
00336
00337
00338 parseCommonAttributes(iterate);
00339
00340 if(handleTags(iterate.toElement(), true))
00341 return true;
00342 iterate = iterate.nextSibling();
00343 }
00344 return true;
00345 }
00346 else if(element.tagName() == "g" || element.tagName() == "defs")
00347 {
00348 TQDomNode iterate = element.firstChild();
00349
00350 while(!iterate.isNull())
00351 {
00352
00353 m_engine->painter()->setWorldMatrix(new TQWMatrix(m_initialMatrix));
00354
00355
00356 parseCommonAttributes(iterate);
00357
00358 handleTags(iterate.toElement(), (element.tagName() == "defs") ? false : true);
00359 iterate = iterate.nextSibling();
00360 }
00361 return true;
00362 }
00363 else if(element.tagName() == "line")
00364 {
00365 double x1 = toPixel(element.attribute("x1"), true);
00366 double y1 = toPixel(element.attribute("y1"), false);
00367 double x2 = toPixel(element.attribute("x2"), true);
00368 double y2 = toPixel(element.attribute("y2"), false);
00369
00370 m_engine->painter()->drawLine(x1, y1, x2, y2);
00371 return true;
00372 }
00373 else if(element.tagName() == "circle")
00374 {
00375 double cx = toPixel(element.attribute("cx"), true);
00376 double cy = toPixel(element.attribute("cy"), false);
00377
00378 double r = toPixel(element.attribute("r"), true);
00379
00380 m_engine->painter()->drawEllipse(cx, cy, r, r);
00381 return true;
00382 }
00383 else if(element.tagName() == "ellipse")
00384 {
00385 double cx = toPixel(element.attribute("cx"), true);
00386 double cy = toPixel(element.attribute("cy"), false);
00387
00388 double rx = toPixel(element.attribute("rx"), true);
00389 double ry = toPixel(element.attribute("ry"), false);
00390
00391 m_engine->painter()->drawEllipse(cx, cy, rx, ry);
00392 return true;
00393 }
00394 else if(element.tagName() == "polyline")
00395 {
00396 TQPointArray polyline = parsePoints(element.attribute("points"));
00397 m_engine->painter()->drawPolyline(polyline);
00398 return true;
00399 }
00400 else if(element.tagName() == "polygon")
00401 {
00402 TQPointArray polygon = parsePoints(element.attribute("points"));
00403 m_engine->painter()->drawPolygon(polygon);
00404 return true;
00405 }
00406 else if(element.tagName() == "path")
00407 {
00408 bool filled = true;
00409
00410 if(element.hasAttribute("fill") && element.attribute("fill").contains("none"))
00411 filled = false;
00412
00413 if(element.attribute("style").contains("fill") && element.attribute("style").stripWhiteSpace().contains("fill:none"))
00414 filled = false;
00415
00416 m_engine->painter()->drawPath(element.attribute("d"), filled);
00417 return true;
00418 }
00419 else if(element.tagName() == "image")
00420 {
00421 double x = toPixel(element.attribute("x"), true);
00422 double y = toPixel(element.attribute("y"), false);
00423 double w = toPixel(element.attribute("width"), true);
00424 double h = toPixel(element.attribute("height"), false);
00425
00426 TQString href = element.attribute("xlink:href");
00427
00428 TQImage image;
00429 if(href.startsWith("data:"))
00430 {
00431
00432 TQCString input = TQString(href.remove(TQRegExp("^data:image/.*;base64,"))).utf8();
00433
00434
00435 TQByteArray output;
00436 KCodecs::base64Decode(input, output);
00437
00438
00439 image.loadFromData(output);
00440 }
00441 else
00442 image.load(href);
00443
00444 if (!image.isNull())
00445 {
00446
00447 if(image.width() != (int) w || image.height() != (int) h)
00448 image = image.smoothScale((int) w, (int) h, TQ_ScaleFree);
00449
00450 m_engine->painter()->drawImage(x, y, image);
00451 }
00452
00453 return true;
00454 }
00455 return false;
00456 }
00457
00458 void parseStyle(const TQString &style)
00459 {
00460 TQStringList substyles = TQStringList::split(';', style);
00461 for(TQStringList::Iterator it = substyles.begin(); it != substyles.end(); ++it)
00462 {
00463 TQStringList substyle = TQStringList::split(':', (*it));
00464 TQString command = substyle[0];
00465 TQString params = substyle[1];
00466 command = command.stripWhiteSpace();
00467 params = params.stripWhiteSpace();
00468
00469 parsePA(command, params);
00470 }
00471 }
00472
00473 void parsePA(const TQString &command, const TQString &value)
00474 {
00475 if(command == "stroke-width")
00476 m_engine->painter()->setStrokeWidth(toPixel(value, false));
00477 else if(command == "stroke-miterlimit")
00478 m_engine->painter()->setStrokeMiterLimit(value);
00479 else if(command == "stroke-linecap")
00480 m_engine->painter()->setCapStyle(value);
00481 else if(command == "stroke-linejoin")
00482 m_engine->painter()->setJoinStyle(value);
00483 else if(command == "stroke-dashoffset")
00484 m_engine->painter()->setStrokeDashOffset(value);
00485 else if(command == "stroke-dasharray" && value != "none")
00486 m_engine->painter()->setStrokeDashArray(value);
00487 else if(command == "stroke")
00488 m_engine->painter()->setStrokeColor(value);
00489 else if(command == "fill")
00490 m_engine->painter()->setFillColor(value);
00491 else if(command == "fill-rule")
00492 m_engine->painter()->setFillRule(value);
00493 else if(command == "fill-opacity" || command == "stroke-opacity" || command == "opacity")
00494 {
00495 if(command == "fill-opacity")
00496 m_engine->painter()->setFillOpacity(value);
00497 else if(command == "stroke-value")
00498 m_engine->painter()->setStrokeOpacity(value);
00499 else
00500 {
00501 m_engine->painter()->setOpacity(value);
00502 m_engine->painter()->setFillOpacity(value);
00503 m_engine->painter()->setStrokeOpacity(value);
00504 }
00505 }
00506 }
00507
00508 private:
00509 friend class KSVGIconEngine;
00510
00511 KSVGIconEngine *m_engine;
00512 TQWMatrix m_initialMatrix;
00513 };
00514
00515 struct KSVGIconEngine::Private
00516 {
00517 KSVGIconPainter *painter;
00518 KSVGIconEngineHelper *helper;
00519
00520 double width;
00521 double height;
00522 };
00523
00524 KSVGIconEngine::KSVGIconEngine() : d(new Private())
00525 {
00526 d->painter = 0;
00527 d->helper = new KSVGIconEngineHelper(this);
00528
00529 d->width = 0.0;
00530 d->height = 0.0;
00531 }
00532
00533 KSVGIconEngine::~KSVGIconEngine()
00534 {
00535 if(d->painter)
00536 delete d->painter;
00537
00538 delete d->helper;
00539
00540 delete d;
00541 }
00542
00543 bool KSVGIconEngine::load(int width, int height, const TQString &path)
00544 {
00545 if(path.isNull()) return false;
00546
00547 TQDomDocument svgDocument("svg");
00548 TQFile file(path);
00549
00550 if(path.right(3).upper() == "SVG")
00551 {
00552
00553 if(!file.open(IO_ReadOnly))
00554 return false;
00555
00556 svgDocument.setContent(&file);
00557 }
00558 else
00559 {
00560 gzFile svgz = gzopen(path.latin1(), "ro");
00561 if(!svgz)
00562 return false;
00563
00564 TQString data;
00565 bool done = false;
00566
00567 TQCString buffer(1024);
00568 int length = 0;
00569
00570 while(!done)
00571 {
00572 int ret = gzread(svgz, buffer.data() + length, 1024);
00573 if(ret == 0)
00574 done = true;
00575 else if(ret == -1)
00576 return false;
00577 else {
00578 buffer.resize(buffer.size()+1024);
00579 length += ret;
00580 }
00581 }
00582
00583 gzclose(svgz);
00584
00585 svgDocument.setContent(buffer);
00586 }
00587
00588 if(svgDocument.isNull())
00589 return false;
00590
00591
00592 TQDomNode rootNode = svgDocument.namedItem("svg");
00593 if(rootNode.isNull() || !rootNode.isElement())
00594 return false;
00595
00596
00597 TQDomElement rootElement = rootNode.toElement();
00598
00599
00600 d->painter = new KSVGIconPainter(width, height);
00601
00602 d->width = width;
00603 if(rootElement.hasAttribute("width"))
00604 d->width = d->helper->toPixel(rootElement.attribute("width"), true);
00605
00606 d->height = height;
00607 if(rootElement.hasAttribute("height"))
00608 d->height = d->helper->toPixel(rootElement.attribute("height"), false);
00609
00610
00611 d->painter->setDrawWidth(static_cast<int>(d->width));
00612 d->painter->setDrawHeight(static_cast<int>(d->height));
00613
00614
00615 d->painter->setClippingRect(0, 0, width, height);
00616
00617
00618 if(rootElement.hasAttribute("viewBox"))
00619 {
00620 TQStringList points = TQStringList::split(' ', rootElement.attribute("viewBox").simplifyWhiteSpace());
00621
00622 float w = points[2].toFloat();
00623 float h = points[3].toFloat();
00624
00625 double vratiow = width / w;
00626 double vratioh = height / h;
00627
00628 d->width = w;
00629 d->height = h;
00630
00631 d->painter->worldMatrix()->scale(vratiow, vratioh);
00632 }
00633 else
00634 {
00635
00636
00637 double ratiow = width / d->width;
00638 double ratioh = height / d->height;
00639
00640 d->painter->worldMatrix()->scale(ratiow, ratioh);
00641 }
00642
00643 TQWMatrix initialMatrix = *d->painter->worldMatrix();
00644 d->helper->m_initialMatrix = initialMatrix;
00645
00646
00647 if(rootElement.hasAttribute("transform"))
00648 d->helper->parseTransform(rootElement.attribute("transform"));
00649
00650
00651 TQDomNode svgNode = rootElement.firstChild();
00652 while(!svgNode.isNull())
00653 {
00654 TQDomElement svgChild = svgNode.toElement();
00655 if(!svgChild.isNull())
00656 {
00657 d->helper->parseCommonAttributes(svgNode);
00658 d->helper->handleTags(svgChild, true);
00659 }
00660
00661 svgNode = svgNode.nextSibling();
00662
00663
00664 d->painter->setWorldMatrix(new TQWMatrix(initialMatrix));
00665 }
00666
00667 d->painter->finish();
00668
00669 return true;
00670 }
00671
00672 KSVGIconPainter *KSVGIconEngine::painter()
00673 {
00674 return d->painter;
00675 }
00676
00677 TQImage *KSVGIconEngine::image()
00678 {
00679 return d->painter->image();
00680 }
00681
00682 double KSVGIconEngine::width()
00683 {
00684 return d->width;
00685 }
00686
00687 double KSVGIconEngine::height()
00688 {
00689 return d->height;
00690 }
00691
00692