• Skip to content
  • Skip to link menu
Trinity API Reference
  • Trinity API Reference
  • tdecore
 

tdecore

  • tdecore
  • svgicons
ksvgiconengine.cpp
1 /*
2  Copyright (C) 2002 Nikolas Zimmermann <wildfox@kde.org>
3  This file is part of the KDE project
4 
5  This library is free software; you can redistribute it and/or
6  modify it under the terms of the GNU Library General Public
7  License as published by the Free Software Foundation; either
8  version 2 of the License, or (at your option) any later version.
9 
10  This library is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  Library General Public License for more details.
14 
15  You should have received a copy of the GNU Library General Public License
16  along with this library; see the file COPYING.LIB. If not, write to
17  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18  Boston, MA 02110-1301, USA.
19 */
20 
21 #include <tqdom.h>
22 #include <tqfile.h>
23 #include <tqcolor.h>
24 #include <tqimage.h>
25 #include <tqwmatrix.h>
26 #include <tqregexp.h>
27 
28 #include <kmdcodec.h>
29 
30 #include <zlib.h>
31 
32 #include "ksvgiconpainter.h"
33 #include "ksvgiconengine.h"
34 
35 class KSVGIconEngineHelper
36 {
37 public:
38  KSVGIconEngineHelper(KSVGIconEngine *engine)
39  {
40  m_engine = engine;
41  }
42 
43  ~KSVGIconEngineHelper()
44  {
45  }
46 
47  double toPixel(const TQString &s, bool hmode)
48  {
49  return m_engine->painter()->toPixel(s, hmode);
50  }
51 
52  ArtGradientStop *parseGradientStops(TQDomElement element, int &offsets)
53  {
54  if (!element.hasChildNodes())
55  return 0;
56 
57  TQValueList<ArtGradientStop> stopList;
58 
59  float oldOffset = -1, newOffset = -1;
60  for(TQDomNode node = element.firstChild(); !node.isNull(); node = node.nextSibling())
61  {
62  TQDomElement element = node.toElement();
63 
64  oldOffset = newOffset;
65  TQString temp = element.attribute("offset");
66 
67  if(temp.contains("%"))
68  {
69  temp = temp.left(temp.length() - 1);
70  newOffset = temp.toFloat() / 100.0;
71  }
72  else
73  newOffset = temp.toFloat();
74 
75  // Spec skip double offset specifications
76  if(oldOffset == newOffset)
77  continue;
78 
79  offsets++;
80  stopList.append(ArtGradientStop());
81 
82  ArtGradientStop &stop = stopList.last();
83 
84  stop.offset = newOffset;
85 
86  TQString parseOpacity;
87  TQString parseColor;
88 
89  if(element.hasAttribute("stop-opacity"))
90  parseOpacity = element.attribute("stop-opacity");
91 
92  if(element.hasAttribute("stop-color"))
93  parseColor = element.attribute("stop-color");
94 
95  if(parseOpacity.isEmpty() || parseColor.isEmpty())
96  {
97  TQString style = element.attribute("style");
98 
99  TQStringList substyles = TQStringList::split(';', style);
100  for(TQStringList::Iterator it = substyles.begin(); it != substyles.end(); ++it)
101  {
102  TQStringList substyle = TQStringList::split(':', (*it));
103  TQString command = substyle[0];
104  TQString params = substyle[1];
105  command = command.stripWhiteSpace();
106  params = params.stripWhiteSpace();
107 
108  if(command == "stop-color")
109  {
110  parseColor = params;
111 
112  if(!parseOpacity.isEmpty())
113  break;
114  }
115  else if(command == "stop-opacity")
116  {
117  parseOpacity = params;
118 
119  if(!parseColor.isEmpty())
120  break;
121  }
122  }
123  }
124 
125  // Parse color using KSVGIconPainter (which uses Qt)
126  // Supports all svg-needed color formats
127  TQColor qStopColor = m_engine->painter()->parseColor(parseColor);
128 
129  // Convert in a libart suitable form
130  TQ_UINT32 stopColor = m_engine->painter()->toArtColor(qStopColor);
131 
132  int opacity = m_engine->painter()->parseOpacity(parseOpacity);
133 
134  TQ_UINT32 rgba = (stopColor << 8) | opacity;
135  TQ_UINT32 r, g, b, a;
136 
137  // Convert from separated to premultiplied alpha
138  a = rgba & 0xff;
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;
145 
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);
150  }
151 
152  if (stopList.isEmpty())
153  return 0;
154 
155  ArtGradientStop *stops = new ArtGradientStop[stopList.count()];
156 
157  TQValueList<ArtGradientStop>::iterator it = stopList.begin();
158  TQValueList<ArtGradientStop>::iterator end = stopList.end();
159 
160  for (int i = 0; it != end; ++i, ++it)
161  stops[i] = *it;
162 
163  return stops;
164  }
165 
166  TQPointArray parsePoints(TQString points)
167  {
168  if(points.isEmpty())
169  return TQPointArray();
170 
171  points = points.simplifyWhiteSpace();
172 
173  if(points.contains(",,") || points.contains(", ,"))
174  return TQPointArray();
175 
176  points.replace(',', ' ');
177  points.replace('\r', TQString());
178  points.replace('\n', TQString());
179 
180  points = points.simplifyWhiteSpace();
181 
182  TQStringList pointList = TQStringList::split(' ', points);
183 
184  TQPointArray array(pointList.count() / 2);
185  int i = 0;
186 
187  for(TQStringList::Iterator it = pointList.begin(); it != pointList.end(); it++)
188  {
189  float x = (*(it++)).toFloat();
190  float y = (*(it)).toFloat();
191 
192  array.setPoint(i, static_cast<int>(x), static_cast<int>(y));
193  i++;
194  }
195 
196  return array;
197  }
198 
199  void parseTransform(const TQString &transform)
200  {
201  // Combine new and old matrix
202  TQWMatrix matrix = m_engine->painter()->parseTransform(transform);
203 
204  TQWMatrix *current = m_engine->painter()->worldMatrix();
205 #ifdef USE_QT4
206 printf("[FIXME] *current = matrix * *current locks up under Qt4; bypassing for now\n");
207 #else // USE_QT4
208  *current = matrix * *current;
209 #endif // USE_QT4
210  }
211 
212  void parseCommonAttributes(TQDomNode &node)
213  {
214  // Set important default attributes
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("");
221  // m_engine->painter()->setFillOpacity(255, true);
222  // m_engine->painter()->setStrokeOpacity(255, true);
223 
224  // Collect parent node's attributes
225  TQPtrList<TQDomNamedNodeMap> applyList;
226  applyList.setAutoDelete(true);
227 
228  TQDomNode shape = node.parentNode();
229  for(; !shape.isNull() ; shape = shape.parentNode())
230  applyList.prepend(new TQDomNamedNodeMap(shape.attributes()));
231 
232  // Apply parent attributes
233  for(TQDomNamedNodeMap *map = applyList.first(); map != 0; map = applyList.next())
234  {
235  TQDomNamedNodeMap attr = *map;
236 
237  for(unsigned int i = 0; i < attr.count(); i++)
238  {
239  TQString name, value;
240 
241  name = attr.item(i).nodeName().lower();
242  value = attr.item(i).nodeValue();
243 
244  if(name == "transform")
245  parseTransform(value);
246  else if(name == "style")
247  parseStyle(value);
248  else
249  parsePA(name, value);
250  }
251  }
252 
253  // Apply local attributes
254  TQDomNamedNodeMap attr = node.attributes();
255 
256  for(unsigned int i = 0; i < attr.count(); i++)
257  {
258  TQDomNode current = attr.item(i);
259 
260  if(current.nodeName().lower() == "transform")
261  parseTransform(current.nodeValue());
262  else if(current.nodeName().lower() == "style")
263  parseStyle(current.nodeValue());
264  else
265  parsePA(current.nodeName().lower(), current.nodeValue());
266  }
267  }
268 
269  bool handleTags(TQDomElement element, bool paint)
270  {
271  if(element.attribute("display") == "none")
272  return false;
273  if(element.tagName() == "linearGradient")
274  {
275  ArtGradientLinear *gradient = new ArtGradientLinear();
276 
277  int offsets = -1;
278  gradient->stops = parseGradientStops(element, offsets);
279  gradient->n_stops = offsets + 1;
280 
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;
286  else
287  gradient->spread = ART_GRADIENT_PAD;
288 
289  m_engine->painter()->addLinearGradient(element.attribute("id"), gradient);
290  m_engine->painter()->addLinearGradientElement(gradient, element);
291  return true;
292  }
293  else if(element.tagName() == "radialGradient")
294  {
295  ArtGradientRadial *gradient = new ArtGradientRadial();
296 
297  int offsets = -1;
298  gradient->stops = parseGradientStops(element, offsets);
299  gradient->n_stops = offsets + 1;
300 
301  m_engine->painter()->addRadialGradient(element.attribute("id"), gradient);
302  m_engine->painter()->addRadialGradientElement(gradient, element);
303  return true;
304  }
305 
306  if(!paint)
307  return true;
308 
309  // TODO: Default attribute values
310  if(element.tagName() == "rect")
311  {
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);
316 
317  double rx = 0.0;
318  double ry = 0.0;
319 
320  if(element.hasAttribute("rx"))
321  rx = toPixel(element.attribute("rx"), true);
322 
323  if(element.hasAttribute("ry"))
324  ry = toPixel(element.attribute("ry"), false);
325 
326  m_engine->painter()->drawRectangle(x, y, w, h, rx, ry);
327  }
328  else if(element.tagName() == "switch")
329  {
330  TQDomNode iterate = element.firstChild();
331 
332  while(!iterate.isNull())
333  {
334  // Reset matrix
335  m_engine->painter()->setWorldMatrix(new TQWMatrix(m_initialMatrix));
336 
337  // Parse common attributes, style / transform
338  parseCommonAttributes(iterate);
339 
340  if(handleTags(iterate.toElement(), true))
341  return true;
342  iterate = iterate.nextSibling();
343  }
344  return true;
345  }
346  else if(element.tagName() == "g" || element.tagName() == "defs")
347  {
348  TQDomNode iterate = element.firstChild();
349 
350  while(!iterate.isNull())
351  {
352  // Reset matrix
353  m_engine->painter()->setWorldMatrix(new TQWMatrix(m_initialMatrix));
354 
355  // Parse common attributes, style / transform
356  parseCommonAttributes(iterate);
357 
358  handleTags(iterate.toElement(), (element.tagName() == "defs") ? false : true);
359  iterate = iterate.nextSibling();
360  }
361  return true;
362  }
363  else if(element.tagName() == "line")
364  {
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);
369 
370  m_engine->painter()->drawLine(x1, y1, x2, y2);
371  return true;
372  }
373  else if(element.tagName() == "circle")
374  {
375  double cx = toPixel(element.attribute("cx"), true);
376  double cy = toPixel(element.attribute("cy"), false);
377 
378  double r = toPixel(element.attribute("r"), true); // TODO: horiz correct?
379 
380  m_engine->painter()->drawEllipse(cx, cy, r, r);
381  return true;
382  }
383  else if(element.tagName() == "ellipse")
384  {
385  double cx = toPixel(element.attribute("cx"), true);
386  double cy = toPixel(element.attribute("cy"), false);
387 
388  double rx = toPixel(element.attribute("rx"), true);
389  double ry = toPixel(element.attribute("ry"), false);
390 
391  m_engine->painter()->drawEllipse(cx, cy, rx, ry);
392  return true;
393  }
394  else if(element.tagName() == "polyline")
395  {
396  TQPointArray polyline = parsePoints(element.attribute("points"));
397  m_engine->painter()->drawPolyline(polyline);
398  return true;
399  }
400  else if(element.tagName() == "polygon")
401  {
402  TQPointArray polygon = parsePoints(element.attribute("points"));
403  m_engine->painter()->drawPolygon(polygon);
404  return true;
405  }
406  else if(element.tagName() == "path")
407  {
408  bool filled = true;
409 
410  if(element.hasAttribute("fill") && element.attribute("fill").contains("none"))
411  filled = false;
412 
413  if(element.attribute("style").contains("fill") && element.attribute("style").stripWhiteSpace().contains("fill:none"))
414  filled = false;
415 
416  m_engine->painter()->drawPath(element.attribute("d"), filled);
417  return true;
418  }
419  else if(element.tagName() == "image")
420  {
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);
425 
426  TQString href = element.attribute("xlink:href");
427 
428  TQImage image;
429  if(href.startsWith("data:"))
430  {
431  // Get input
432  TQCString input = TQString(href.remove(TQRegExp("^data:image/.*;base64,"))).utf8();
433 
434  // Decode into 'output'
435  TQByteArray output;
436  KCodecs::base64Decode(input, output);
437 
438  // Display
439  image.loadFromData(output);
440  }
441  else
442  image.load(href);
443 
444  if (!image.isNull())
445  {
446  // Scale, if needed
447  if(image.width() != (int) w || image.height() != (int) h)
448  image = image.smoothScale((int) w, (int) h, TQ_ScaleFree);
449 
450  m_engine->painter()->drawImage(x, y, image);
451  }
452 
453  return true;
454  }
455  return false;
456  }
457 
458  void parseStyle(const TQString &style)
459  {
460  TQStringList substyles = TQStringList::split(';', style);
461  for(TQStringList::Iterator it = substyles.begin(); it != substyles.end(); ++it)
462  {
463  TQStringList substyle = TQStringList::split(':', (*it));
464  TQString command = substyle[0];
465  TQString params = substyle[1];
466  command = command.stripWhiteSpace();
467  params = params.stripWhiteSpace();
468 
469  parsePA(command, params);
470  }
471  }
472 
473  void parsePA(const TQString &command, const TQString &value)
474  {
475  if(command == "stroke-width") // TODO: horiz:false correct?
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")
494  {
495  if(command == "fill-opacity")
496  m_engine->painter()->setFillOpacity(value);
497  else if(command == "stroke-value")
498  m_engine->painter()->setStrokeOpacity(value);
499  else
500  {
501  m_engine->painter()->setOpacity(value);
502  m_engine->painter()->setFillOpacity(value);
503  m_engine->painter()->setStrokeOpacity(value);
504  }
505  }
506  }
507 
508 private:
509  friend class KSVGIconEngine;
510 
511  KSVGIconEngine *m_engine;
512  TQWMatrix m_initialMatrix;
513 };
514 
515 struct KSVGIconEngine::Private
516 {
517  KSVGIconPainter *painter;
518  KSVGIconEngineHelper *helper;
519 
520  double width;
521  double height;
522 };
523 
524 KSVGIconEngine::KSVGIconEngine() : d(new Private())
525 {
526  d->painter = 0;
527  d->helper = new KSVGIconEngineHelper(this);
528 
529  d->width = 0.0;
530  d->height = 0.0;
531 }
532 
533 KSVGIconEngine::~KSVGIconEngine()
534 {
535  if(d->painter)
536  delete d->painter;
537 
538  delete d->helper;
539 
540  delete d;
541 }
542 
543 bool KSVGIconEngine::load(int width, int height, const TQString &path)
544 {
545  if(path.isNull()) return false;
546 
547  TQDomDocument svgDocument("svg");
548  TQFile file(path);
549 
550  if(path.right(3).upper() == "SVG")
551  {
552  // Open SVG Icon
553  if(!file.open(IO_ReadOnly))
554  return false;
555 
556  svgDocument.setContent(&file);
557  }
558  else // SVGZ
559  {
560  gzFile svgz = gzopen(path.latin1(), "ro");
561  if(!svgz)
562  return false;
563 
564  TQString data;
565  bool done = false;
566 
567  TQCString buffer(1024);
568  int length = 0;
569 
570  while(!done)
571  {
572  int ret = gzread(svgz, buffer.data() + length, 1024);
573  if(ret == 0)
574  done = true;
575  else if(ret == -1)
576  return false;
577  else {
578  buffer.resize(buffer.size()+1024);
579  length += ret;
580  }
581  }
582 
583  gzclose(svgz);
584 
585  svgDocument.setContent(buffer);
586  }
587 
588  if(svgDocument.isNull())
589  return false;
590 
591  // Check for root element
592  TQDomNode rootNode = svgDocument.namedItem("svg");
593  if(rootNode.isNull() || !rootNode.isElement())
594  return false;
595 
596  // Detect width and height
597  TQDomElement rootElement = rootNode.toElement();
598 
599  // Create icon painter
600  d->painter = new KSVGIconPainter(width, height);
601 
602  d->width = width; // this sets default for no width -> 100% case
603  if(rootElement.hasAttribute("width"))
604  d->width = d->helper->toPixel(rootElement.attribute("width"), true);
605 
606  d->height = height; // this sets default for no height -> 100% case
607  if(rootElement.hasAttribute("height"))
608  d->height = d->helper->toPixel(rootElement.attribute("height"), false);
609 
610  // Create icon painter
611  d->painter->setDrawWidth(static_cast<int>(d->width));
612  d->painter->setDrawHeight(static_cast<int>(d->height));
613 
614  // Set viewport clipping rect
615  d->painter->setClippingRect(0, 0, width, height);
616 
617  // Apply viewbox
618  if(rootElement.hasAttribute("viewBox"))
619  {
620  TQStringList points = TQStringList::split(' ', rootElement.attribute("viewBox").simplifyWhiteSpace());
621 
622  float w = points[2].toFloat();
623  float h = points[3].toFloat();
624 
625  double vratiow = width / w;
626  double vratioh = height / h;
627 
628  d->width = w;
629  d->height = h;
630 
631  d->painter->worldMatrix()->scale(vratiow, vratioh);
632  }
633  else
634  {
635  // Fit into 'width' and 'height'
636  // FIXME: Use an aspect ratio
637  double ratiow = width / d->width;
638  double ratioh = height / d->height;
639 
640  d->painter->worldMatrix()->scale(ratiow, ratioh);
641  }
642 
643  TQWMatrix initialMatrix = *d->painter->worldMatrix();
644  d->helper->m_initialMatrix = initialMatrix;
645 
646  // Apply transform
647  if(rootElement.hasAttribute("transform"))
648  d->helper->parseTransform(rootElement.attribute("transform"));
649 
650  // Go through all elements
651  TQDomNode svgNode = rootElement.firstChild();
652  while(!svgNode.isNull())
653  {
654  TQDomElement svgChild = svgNode.toElement();
655  if(!svgChild.isNull())
656  {
657  d->helper->parseCommonAttributes(svgNode);
658  d->helper->handleTags(svgChild, true);
659  }
660 
661  svgNode = svgNode.nextSibling();
662 
663  // Reset matrix
664  d->painter->setWorldMatrix(new TQWMatrix(initialMatrix));
665  }
666 
667  d->painter->finish();
668 
669  return true;
670 }
671 
672 KSVGIconPainter *KSVGIconEngine::painter()
673 {
674  return d->painter;
675 }
676 
677 TQImage *KSVGIconEngine::image()
678 {
679  return d->painter->image();
680 }
681 
682 double KSVGIconEngine::width()
683 {
684  return d->width;
685 }
686 
687 double KSVGIconEngine::height()
688 {
689  return d->height;
690 }
KCodecs::base64Decode
static TQCString base64Decode(const TQByteArray &in)
Decodes the given data that was encoded using the base64 algorithm.
Definition: kmdcodec.cpp:462
TDEStdAccel::end
const TDEShortcut & end()
Goto end of the document.
Definition: tdestdaccel.cpp:289
KStdAction::name
const char * name(StdAction id)

tdecore

Skip menu "tdecore"
  • Main Page
  • Modules
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

tdecore

Skip menu "tdecore"
  • arts
  • dcop
  • dnssd
  • interfaces
  •   kspeech
  •     interface
  •     library
  •   tdetexteditor
  • kate
  • kded
  • kdoctools
  • kimgio
  • kjs
  • libtdemid
  • libtdescreensaver
  •     tdecore
  • tdeabc
  • tdecmshell
  • tdecore
  • tdefx
  • tdehtml
  • tdeinit
  • tdeio
  •   bookmarks
  •   httpfilter
  •   kpasswdserver
  •   kssl
  • tdeioslave
  •   http
  •   tdefile
  •   tdeio
  •   tdeioexec
  • tdemdi
  •   tdemdi
  • tdenewstuff
  • tdeparts
  • tdeprint
  • tderandr
  • tderesources
  • tdespell2
  • tdesu
  • tdeui
  • tdeunittest
  • tdeutils
  • tdewallet
Generated for tdecore by doxygen 1.8.8
This website is maintained by Timothy Pearson.