00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 #include <private/qpainter_p.h>
00025 #include <private/qpaintengine_p.h>
00026 #include "qfile.h"
00027 #include "qimage.h"
00028 #include "qlist.h"
00029 #include "qmap.h"
00030 #include "q3paintengine_svg_p.h"
00031 #include "qpainter.h"
00032 #include "qpixmap.h"
00033 #include "qregexp.h"
00034 #include "qtextstream.h"
00035
00036 #include <math.h>
00037
00038 static const double deg2rad = 0.017453292519943295769;
00039 static const char piData[] = "version=\"1.0\" standalone=\"no\"";
00040 static const char publicId[] = "-//W3C//DTD SVG 20001102//EN";
00041 static const char systemId[] = "http://www.w3.org/TR/2000/CR-SVG-20001102/DTD/svg-20001102.dtd";
00042
00043 static QString qt_svg_compose_path(const QPainterPath &path);
00044
00045 struct QImgElement {
00046 QDomElement element;
00047 QImage image;
00048 Q_DUMMY_COMPARISON_OPERATOR(QImgElement)
00049 };
00050
00051 struct QPixElement {
00052 QDomElement element;
00053 QPixmap pixmap;
00054 Q_DUMMY_COMPARISON_OPERATOR(QPixElement)
00055 };
00056
00057 struct Q3SVGPaintEngineState {
00058 double textx, texty;
00059 int textalign;
00060 Q_DUMMY_COMPARISON_OPERATOR(Q3SVGPaintEngineState)
00061 };
00062
00063 typedef QList<QImgElement> ImageList;
00064 typedef QList<QPixElement> PixmapList;
00065 typedef QList<Q3SVGPaintEngineState> StateList;
00066
00067 enum ElementType {
00068 InvalidElement = 0,
00069 AnchorElement,
00070 CircleElement,
00071 ClipElement,
00072 CommentElement,
00073 DescElement,
00074 EllipseElement,
00075 GroupElement,
00076 ImageElement,
00077 LineElement,
00078 PolylineElement,
00079 PolygonElement,
00080 PathElement,
00081 RectElement,
00082 SvgElement,
00083 TextElement,
00084 TitleElement,
00085 TSpanElement
00086 };
00087
00088 typedef QMap<QString,ElementType> QSvgTypeMap;
00089 static QSvgTypeMap *qSvgTypeMap=0;
00090 static QMap<QString,QString> *qSvgColMap=0;
00091
00092 class Q3SVGPaintEnginePrivate : public QPaintEnginePrivate
00093 {
00094 Q_DECLARE_PUBLIC(Q3SVGPaintEngine)
00095
00096 public:
00097 Q3SVGPaintEnginePrivate()
00098 : dirtyTransform(false), dirtyStyle(false), currentClip(0),
00099 dev(0), wwidth(0), wheight(0) {}
00100 void appendChild(QDomElement &e, QPicturePrivate::PaintCommand c);
00101 void applyStyle(QDomElement *e, QPicturePrivate::PaintCommand c) const;
00102 void applyTransform(QDomElement *e) const;
00103 double parseLen(const QString &str, bool *ok=0, bool horiz=true) const;
00104 int lenToInt(const QDomNamedNodeMap &map, const QString &attr, int def = 0) const;
00105 double lenToDouble(const QDomNamedNodeMap &map, const QString &attr, int def = 0) const;
00106 bool play(const QDomNode &node, QPainter *p);
00107 void setTransform(const QString &tr, QPainter *p);
00108 void restoreAttributes(QPainter *p);
00109 void saveAttributes(QPainter *p);
00110 void setStyle(const QString &s, QPainter *p);
00111 void setStyleProperty(const QString &prop, const QString &val, QPen *pen, QFont *font,
00112 int *talign, QPainter *p);
00113 void drawPath(const QString &data, QPainter *p);
00114 QColor parseColor(const QString &col);
00115 void init() {
00116 QDomImplementation domImpl;
00117 QDomDocumentType docType = domImpl.createDocumentType("svg", publicId, systemId);
00118 doc = domImpl.createDocument("http://www.w3.org/2000/svg", "svg", docType);
00119 doc.insertBefore(doc.createProcessingInstruction("xml", piData), doc.firstChild());
00120 current = doc.documentElement();
00121 images.clear();
00122 pixmaps.clear();
00123
00124 doc.documentElement().setAttribute("xmlns:xlink", "http://www.w3.org/1999/xlink");
00125 }
00126
00127
00128 bool dirtyTransform;
00129 bool dirtyStyle;
00130 QRect brect;
00131 QDomDocument doc;
00132 QDomNode current;
00133
00134 ImageList images;
00135 PixmapList pixmaps;
00136 StateList stack;
00137 int currentClip;
00138
00139
00140 Q3SVGPaintEngineState *curr;
00141
00142 QPen cpen;
00143 QBrush cbrush;
00144 QFont cfont;
00145 QMatrix worldMatrix;
00146 const QPaintDevice *dev;
00147 int wwidth;
00148 int wheight;
00149 };
00150
00151 Q3SVGPaintEngine::Q3SVGPaintEngine()
00152 : QPaintEngine(*(new Q3SVGPaintEnginePrivate), AllFeatures)
00153 {
00154 Q_D(Q3SVGPaintEngine);
00155 d->init();
00156 }
00157
00158 Q3SVGPaintEngine::Q3SVGPaintEngine(Q3SVGPaintEnginePrivate &dptr)
00159 : QPaintEngine(dptr, AllFeatures)
00160 {
00161 Q_D(Q3SVGPaintEngine);
00162 d->init();
00163 }
00164
00165 Q3SVGPaintEngine::~Q3SVGPaintEngine()
00166 {
00167 delete qSvgTypeMap; qSvgTypeMap = 0;
00168 delete qSvgColMap; qSvgColMap = 0;
00169 }
00170
00171 bool Q3SVGPaintEngine::begin(QPaintDevice *pdev)
00172 {
00173 Q_D(Q3SVGPaintEngine);
00174 d->dirtyTransform = d->dirtyStyle = false;
00175 d->dev = pdev;
00176 setActive(true);
00177 return true;
00178 }
00179
00180 bool Q3SVGPaintEngine::end()
00181 {
00182 Q_D(Q3SVGPaintEngine);
00183 d->dev = 0;
00184 setActive(false);
00185 return true;
00186 }
00187
00188 void Q3SVGPaintEngine::updateState(const QPaintEngineState &state)
00189 {
00190 QPaintEngine::DirtyFlags flags = state.state();
00191 if (flags & DirtyPen) updatePen(state.pen());
00192 if ((flags & DirtyBrush) || (flags & DirtyBrushOrigin))
00193 updateBrush(state.brush(), state.brushOrigin());
00194 if (flags & DirtyBackground) updateBackground(state.backgroundMode(), state.backgroundBrush());
00195 if (flags & DirtyFont) updateFont(state.font());
00196 if (flags & DirtyTransform) updateMatrix(state.matrix());
00197 if (flags & DirtyClipRegion) updateClipRegion(state.clipRegion(), state.clipOperation());
00198 if (flags & DirtyClipPath) updateClipPath(state.clipPath(), state.clipOperation());
00199 }
00200
00201 void Q3SVGPaintEngine::updatePen(const QPen &pen)
00202 {
00203 Q_D(Q3SVGPaintEngine);
00204 d->cpen = pen;
00205 d->dirtyStyle = true;
00206 }
00207
00208 void Q3SVGPaintEngine::updateBrush(const QBrush &brush, const QPointF &)
00209 {
00210 Q_D(Q3SVGPaintEngine);
00211 d->cbrush = brush;
00212 d->dirtyStyle = true;
00213 }
00214
00215 void Q3SVGPaintEngine::updateFont(const QFont &font)
00216 {
00217 Q_D(Q3SVGPaintEngine);
00218 d->cfont = font;
00219 d->dirtyStyle = true;
00220 }
00221
00222 void Q3SVGPaintEngine::updateBackground(Qt::BGMode, const QBrush &)
00223 {
00224 Q_D(Q3SVGPaintEngine);
00225 d->dirtyStyle = true;
00226 }
00227
00228 void Q3SVGPaintEngine::updateMatrix(const QMatrix &matrix)
00229 {
00230 Q_D(Q3SVGPaintEngine);
00231 d->dirtyTransform = true;
00232 d->worldMatrix = matrix;
00233
00234
00235 }
00236
00237 void Q3SVGPaintEngine::updateClipPath(const QPainterPath &path, Qt::ClipOperation op)
00238 {
00239 Q_D(Q3SVGPaintEngine);
00240 if (op == Qt::NoClip)
00241 return;
00242
00243 QDomElement e;
00244 d->currentClip++;
00245 e = d->doc.createElement("clipPath");
00246 e.setAttribute("id", QString("clip%1").arg(d->currentClip));
00247
00248 QDomElement path_element = d->doc.createElement("path");
00249 path_element.setAttribute("d", qt_svg_compose_path(path));
00250 e.appendChild(path_element);
00251
00252 d->appendChild(e, QPicturePrivate::PdcSetClipPath);
00253 }
00254
00255 void Q3SVGPaintEngine::updateClipRegion(const QRegion &clipRegion, Qt::ClipOperation op)
00256 {
00257 QPainterPath clipPath;
00258 clipPath.addRegion(clipRegion);
00259 updateClipPath(clipPath, op);
00260 }
00261
00262 void Q3SVGPaintEngine::updateRenderHints(QPainter::RenderHints)
00263 {
00264 }
00265
00266 void Q3SVGPaintEngine::drawRect(const QRectF &r)
00267 {
00268 Q_D(Q3SVGPaintEngine);
00269 QDomElement e;
00270 e = d->doc.createElement("rect");
00271
00272 e.setAttribute("x", r.x());
00273 e.setAttribute("y", r.y());
00274 e.setAttribute("width", r.width());
00275 e.setAttribute("height", r.height());
00276 d->appendChild(e, QPicturePrivate::PdcDrawRect);
00277 }
00278
00279 void Q3SVGPaintEngine::drawPoint(const QPointF &p)
00280 {
00281 QLineF l(p, p);
00282 drawLines(&l, 1);
00283 }
00284
00285 void Q3SVGPaintEngine::drawPoints(const QPointF *points, int pointCount)
00286 {
00287 for (int i = 0; i < pointCount; ++i) {
00288 QLineF l(points[i], points[i]);
00289 drawLines(&l, 1);
00290 }
00291 }
00292
00293 void Q3SVGPaintEngine::drawEllipse(const QRect &r)
00294 {
00295 Q_D(Q3SVGPaintEngine);
00296 QDomElement e;
00297
00298 if (r.width() == r.height()) {
00299 e = d->doc.createElement("circle");
00300 double cx = r.x() + (r.width() / 2.0);
00301 double cy = r.y() + (r.height() / 2.0);
00302 e.setAttribute("cx", cx);
00303 e.setAttribute("cy", cy);
00304 e.setAttribute("r", cx - r.x());
00305 } else {
00306 e = d->doc.createElement("ellipse");
00307 double cx = r.x() + (r.width() / 2.0);
00308 double cy = r.y() + (r.height() / 2.0);
00309 e.setAttribute("cx", cx);
00310 e.setAttribute("cy", cy);
00311 e.setAttribute("rx", cx - r.x());
00312 e.setAttribute("ry", cy - r.y());
00313 }
00314 d->appendChild(e, QPicturePrivate::PdcDrawEllipse);
00315 }
00316
00317 void Q3SVGPaintEngine::drawLine(const QLineF &line)
00318 {
00319 drawLines(&line, 1);
00320 }
00321
00322 void Q3SVGPaintEngine::drawLines(const QLineF *lines, int lineCount)
00323 {
00324 Q_D(Q3SVGPaintEngine);
00325 QDomElement e;
00326
00327 for (int i = 0; i < lineCount; ++i) {
00328 e = d->doc.createElement("line");
00329 e.setAttribute("x1", lines[i].x1());
00330 e.setAttribute("y1", lines[i].y1());
00331 e.setAttribute("x2", lines[i].x2());
00332 e.setAttribute("y2", lines[i].y2());
00333 d->appendChild(e, QPicturePrivate::PdcDrawLineSegments);
00334 }
00335 }
00336
00337 void Q3SVGPaintEngine::drawPath(const QPainterPath &path)
00338 {
00339 Q_D(Q3SVGPaintEngine);
00340 QDomElement e = d->doc.createElement("path");
00341 e.setAttribute("d", qt_svg_compose_path(path));
00342 d->appendChild(e, QPicturePrivate::PdcDrawPath);
00343 }
00344
00345 void Q3SVGPaintEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
00346 {
00347 Q_D(Q3SVGPaintEngine);
00348 QString str;
00349 if (mode == PolylineMode) {
00350 QDomElement e = d->doc.createElement("polyline");
00351 for (int i = 0; i < pointCount; ++i) {
00352 QString tmp;
00353 tmp.sprintf("%f %f ", points[i].x(), points[i].y());
00354 str += tmp;
00355 }
00356 e.setAttribute("points", str.trimmed());
00357 d->appendChild(e, QPicturePrivate::PdcDrawPolyline);
00358 } else {
00359 QDomElement e = d->doc.createElement("polygon");
00360 for (int i = 0; i < pointCount; ++i) {
00361 QString tmp;
00362 tmp.sprintf("%f %f ", points[i].x(), points[i].y());
00363 str += tmp;
00364 }
00365 e.setAttribute("points", str.trimmed());
00366 d->appendChild(e, QPicturePrivate::PdcDrawPolygon);
00367 }
00368 }
00369
00370 void Q3SVGPaintEngine::drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode)
00371 {
00372 QPolygonF poly;
00373 for (int i = 0; i < pointCount; ++i)
00374 poly << points[i];
00375 drawPolygon(poly.constData(), pointCount, mode);
00376 }
00377
00378 void Q3SVGPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF & )
00379 {
00380 Q_D(Q3SVGPaintEngine);
00381 QDomElement e = d->doc.createElement("image");
00382 e.setAttribute("x", r.x());
00383 e.setAttribute("y", r.y());
00384 e.setAttribute("width", r.width());
00385 e.setAttribute("height", r.height());
00386
00387 QPixElement pe;
00388 pe.element = e;
00389 pe.pixmap = pm;
00390 d->pixmaps.append(pe);
00391
00392
00393
00394 d->appendChild(e, QPicturePrivate::PdcDrawPixmap);
00395 }
00396
00397 void Q3SVGPaintEngine::drawTiledPixmap(const QRectF & , const QPixmap & ,
00398 const QPointF & )
00399 {
00400 }
00401
00402 void Q3SVGPaintEngine::drawTextItem(const QPointF &p, const QTextItem &ti)
00403 {
00404 Q_D(Q3SVGPaintEngine);
00405 QDomElement e = d->doc.createElement("text");
00406
00407
00408
00409
00410
00411
00412
00413
00414
00415
00416
00417
00418
00419
00420
00421
00422
00423
00424
00425
00426
00427
00428
00429 e.setAttribute("x", p.x());
00430 e.setAttribute("y", p.y());
00431 e.appendChild(d->doc.createTextNode(ti.text()));
00432 }
00433
00434 void Q3SVGPaintEngine::drawImage(const QRectF &r, const QImage &im,
00435 const QRectF &, Qt::ImageConversionFlags)
00436 {
00437 Q_D(Q3SVGPaintEngine);
00438 QDomElement e = d->doc.createElement("image");
00439 e.setAttribute("x", r.x());
00440 e.setAttribute("y", r.y());
00441 e.setAttribute("width", r.width());
00442 e.setAttribute("height", r.height());
00443 QImgElement ie;
00444 ie.element = e;
00445 ie.image = im;
00446 d->images.append(ie);
00447
00448
00449 d->appendChild(e, QPicturePrivate::PdcDrawImage);
00450 }
00451
00452
00457 QString Q3SVGPaintEngine::toString() const
00458 {
00459 Q_D(const Q3SVGPaintEngine);
00460 if (d->doc.isNull())
00461 return QString();
00462
00463 return d->doc.toString();
00464 }
00465
00470 bool Q3SVGPaintEngine::save(const QString &fileName)
00471 {
00472 Q_D(Q3SVGPaintEngine);
00473
00474 QString svgName = fileName.endsWith(".svg") ?
00475 fileName.left(fileName.length()-4) : fileName;
00476
00477
00478 QDomElement root = d->doc.documentElement();
00479 root.setAttribute("id", svgName);
00480
00481
00482
00483 root.setAttribute("x", d->brect.x());
00484 root.setAttribute("y", d->brect.y());
00485 root.setAttribute("width", d->brect.width() + d->brect.x());
00486 root.setAttribute("height", d->brect.height() + d->brect.y());
00487
00488
00489 int icount = 0;
00490 ImageList::Iterator iit = d->images.begin();
00491 for (; iit != d->images.end(); ++iit) {
00492 QString href = QString("%1_%2.png").arg(svgName).arg(icount);
00493 (*iit).image.save(href, "PNG");
00494 (*iit).element.setAttribute("xlink:href", href);
00495 icount++;
00496 }
00497 PixmapList::Iterator pit = d->pixmaps.begin();
00498 for (; pit != d->pixmaps.end(); ++pit) {
00499 QString href = QString("%1_%2.png").arg(svgName).arg(icount);
00500 (*pit).pixmap.save(href, "PNG");
00501 (*pit).element.setAttribute("xlink:href", href);
00502 icount++;
00503 }
00504
00505 QFile f(fileName);
00506 if (!f.open (QIODevice::WriteOnly))
00507 return false;
00508 QTextStream s(&f);
00509 s.setEncoding(QTextStream::UnicodeUTF8);
00510 s << d->doc;
00511
00512 return true;
00513 }
00514
00521 bool Q3SVGPaintEngine::save(QIODevice *dev)
00522 {
00523 Q_D(Q3SVGPaintEngine);
00524 #if defined(CHECK_RANGE)
00525 if (!d->images.isEmpty() || !d->pixmaps.isEmpty())
00526 qWarning("Q3SVGPaintEngine::save: skipping external images");
00527 #endif
00528
00529 QTextStream s(dev);
00530 s.setEncoding(QTextStream::UnicodeUTF8);
00531 s << d->doc;
00532
00533 return true;
00534 }
00535
00540 void Q3SVGPaintEngine::setBoundingRect(const QRect &r)
00541 {
00542 d_func()->brect = r;
00543 }
00544
00549 QRect Q3SVGPaintEngine::boundingRect() const
00550 {
00551 return d_func()->brect;
00552 }
00553
00560 bool Q3SVGPaintEngine::load(QIODevice *dev)
00561 {
00562 return d_func()->doc.setContent(dev);
00563 }
00564
00565 void Q3SVGPaintEnginePrivate::appendChild(QDomElement &e, QPicturePrivate::PaintCommand c)
00566 {
00567 if (!e.isNull()) {
00568 current.appendChild(e);
00569 if (c == QPicturePrivate::PdcSave)
00570 current = e;
00571
00572 if (c == QPicturePrivate::PdcSetClipRegion || c == QPicturePrivate::PdcSetClipPath) {
00573 QDomElement ne;
00574 ne = doc.createElement("g");
00575 ne.setAttribute("style", QString("clip-path:url(#clip%1)").arg(currentClip));
00576 if (dirtyTransform) {
00577 applyTransform(&ne);
00578 dirtyTransform = false;
00579 }
00580 current.appendChild(ne);
00581 current = ne;
00582 } else {
00583 if (dirtyStyle)
00584 applyStyle(&e, c);
00585 if (dirtyTransform && e.tagName() != "g") {
00586
00587 applyTransform(&e);
00588 if (c == QPicturePrivate::PdcSave)
00589 dirtyTransform = false;
00590 }
00591 }
00592 }
00593 }
00594
00595 void Q3SVGPaintEnginePrivate::applyStyle(QDomElement *e, QPicturePrivate::PaintCommand c) const
00596 {
00597
00598 QColor pcol = cpen.color();
00599 QColor bcol = cbrush.color();
00600 QString s;
00601 if (c == QPicturePrivate::PdcDrawText2 || c == QPicturePrivate::PdcDrawText2Formatted) {
00602
00603
00604 s += QString("fill:rgb(%1,%2,%3);").arg(pcol.red()).arg(pcol.green()).arg(pcol.blue());
00605 s += QString("stroke-width:0;");
00606 QFont f = cfont;
00607 QFontInfo fi(f);
00608 s += QString("font-size:%1;").arg(fi.pointSize());
00609 s += QString("font-style:%1;").arg(f.italic() ? "italic" : "normal");
00610
00611 QString fw;
00612 if (f.weight() <= QFont::Light)
00613 fw = "100";
00614 else if (f.weight() <= QFont::Normal)
00615 fw = "400";
00616 else if (f.weight() <= QFont::DemiBold)
00617 fw = "600";
00618 else if (f.weight() <= QFont::Bold)
00619 fw = "700";
00620 else if (f.weight() <= QFont::Black)
00621 fw = "800";
00622 else
00623 fw = "900";
00624 s += QString("font-weight:%1;").arg(fw);
00625 s += QString("font-family:%1;").arg(f.family());
00626 } else {
00627 s += QString("stroke:rgb(%1,%2,%3);").arg(pcol.red()).arg(pcol.green()).arg(pcol.blue());
00628 if (pcol.alpha() != 255)
00629 s += QString("stroke-opacity:%1;").arg(pcol.alpha()/255.0);
00630 if (bcol.alpha() != 255)
00631 s += QString("fill-opacity:%1;").arg(bcol.alpha()/255.0);
00632 double pw = cpen.width();
00633 if (pw == 0 && cpen.style() != Qt::NoPen)
00634 pw = 0.9;
00635 if (c == QPicturePrivate::PdcDrawLine)
00636 pw /= (qAbs(worldMatrix.m11()) + qAbs(worldMatrix.m22())) / 2.0;
00637 s += QString("stroke-width:%1;").arg(pw);
00638 if (cpen.style() == Qt::DashLine)
00639 s+= QString("stroke-dasharray:18,6;");
00640 else if (cpen.style() == Qt::DotLine)
00641 s+= QString("stroke-dasharray:3;");
00642 else if (cpen.style() == Qt::DashDotLine)
00643 s+= QString("stroke-dasharray:9,6,3,6;");
00644 else if (cpen.style() == Qt::DashDotDotLine)
00645 s+= QString("stroke-dasharray:9,3,3;");
00646 if (cbrush.style() == Qt::NoBrush || c == QPicturePrivate::PdcDrawPolyline || c == QPicturePrivate::PdcDrawCubicBezier)
00647 s += "fill:none;";
00648 else
00649 s += QString("fill:rgb(%1,%2,%3);").arg(bcol.red()).arg(bcol.green()).arg(bcol.blue());
00650 }
00651 e->setAttribute("style", s);
00652 }
00653
00654 void Q3SVGPaintEnginePrivate::applyTransform(QDomElement *e) const
00655 {
00656 QMatrix m = worldMatrix;
00657
00658 QString s;
00659 bool rot = (m.m11() != 1.0 || m.m12() != 0.0 ||
00660 m.m21() != 0.0 || m.m22() != 1.0);
00661 if (!rot && (m.dx() != 0.0 || m.dy() != 0.0)) {
00662 s = QString("translate(%1,%2)").arg(m.dx()).arg(m.dy());
00663 } else if (rot) {
00664 if (m.m12() == 0.0 && m.m21() == 0.0 &&
00665 m.dx() == 0.0 && m.dy() == 0.0)
00666 s = QString("scale(%1,%2)").arg(m.m11()).arg(m.m22());
00667 else
00668 s = QString("matrix(%1,%2,%3,%4,%5,%6)")
00669 .arg(m.m11()).arg(m.m12())
00670 .arg(m.m21()).arg(m.m22())
00671 .arg(m.dx()).arg(m.dy());
00672 } else {
00673 return;
00674 }
00675 e->setAttribute("transform", s);
00676 }
00677
00678 bool Q3SVGPaintEngine::play(QPainter *pt)
00679 {
00680 Q_D(Q3SVGPaintEngine);
00681 if (!pt) {
00682 Q_ASSERT(pt);
00683 return false;
00684 }
00685 if (d->dev == 0)
00686 d->dev = pt->device();
00687 d->wwidth = pt->window().width();
00688 d->wheight = pt->window().height();
00689
00690 pt->setPen(Qt::NoPen);
00691 pt->setBrush(Qt::black);
00692 if (d->doc.isNull()) {
00693 qWarning("Q3SVGPaintEngine::play: No SVG data set.");
00694 return false;
00695 }
00696
00697 QDomNode svg = d->doc.namedItem("svg");
00698 if (svg.isNull() || !svg.isElement()) {
00699 qWarning("Q3SVGPaintEngine::play: Couldn't find any svg element.");
00700 return false;
00701 }
00702
00703
00704
00705 pt->setWorldXForm(true);
00706
00707 QDomNamedNodeMap attr = svg.attributes();
00708 int x = d->lenToInt(attr, "x");
00709 int y = d->lenToInt(attr, "y");
00710 d->brect.setX(x);
00711 d->brect.setY(y);
00712 QString wstr = attr.contains("width")
00713 ? attr.namedItem("width").nodeValue() : QString("100%");
00714 QString hstr = attr.contains("height")
00715 ? attr.namedItem("height").nodeValue() : QString("100%");
00716 double width = d->parseLen(wstr, 0, true);
00717 double height = d->parseLen(hstr, 0, false);
00718
00719 d->brect.setWidth(int(width) - x);
00720 d->brect.setHeight(int(height) - y);
00721 pt->setClipRect(d->brect);
00722
00723 if (attr.contains("viewBox")) {
00724 QRegExp re(QString::fromLatin1("\\s*(\\S+)\\s*,?\\s*(\\S+)\\s*,?"
00725 "\\s*(\\S+)\\s*,?\\s*(\\S+)\\s*"));
00726 if (re.indexIn(attr.namedItem("viewBox").nodeValue()) < 0) {
00727 qWarning("Q3SVGPaintEngine::play: Invalid viewBox attribute.");
00728 return false;
00729 } else {
00730 double x = re.cap(1).toDouble();
00731 double y = re.cap(2).toDouble();
00732 double w = re.cap(3).toDouble();
00733 double h = re.cap(4).toDouble();
00734 if (w < 0 || h < 0) {
00735 qWarning("Q3SVGPaintEngine::play: Invalid viewBox dimension.");
00736 return false;
00737 } else if (w == 0 || h == 0) {
00738 return true;
00739 }
00740 pt->scale(width/w, height/h);
00741 pt->translate(-x, -y);
00742 }
00743 }
00744
00745 const struct ElementTable {
00746 const char *name;
00747 ElementType type;
00748 } etab[] = {
00749 {"a", AnchorElement },
00750 {"#comment", CommentElement },
00751 {"circle", CircleElement },
00752 {"clipPath", ClipElement },
00753 {"desc", DescElement },
00754 {"ellipse", EllipseElement },
00755 {"g", GroupElement },
00756 {"image", ImageElement },
00757 {"line", LineElement },
00758 {"polyline", PolylineElement},
00759 {"polygon", PolygonElement },
00760 {"path", PathElement },
00761 {"rect", RectElement },
00762 {"svg", SvgElement },
00763 {"text", TextElement },
00764 {"tspan", TSpanElement },
00765 {"title", TitleElement },
00766 {0, InvalidElement }
00767 };
00768
00769 if (!qSvgTypeMap) {
00770 qSvgTypeMap = new QSvgTypeMap;
00771 const ElementTable *t = etab;
00772 while (t->name) {
00773 qSvgTypeMap->insert(t->name, t->type);
00774 t++;
00775 }
00776 }
00777
00778
00779 Q3SVGPaintEngineState st;
00780 st.textx = st.texty = 0;
00781 st.textalign = Qt::AlignLeft;
00782 d->stack.append(st);
00783 d->curr = &d->stack.last();
00784
00785 bool b = d->play(svg, pt);
00786 d->stack.removeFirst();
00787 return b;
00788 }
00789
00790 bool Q3SVGPaintEnginePrivate::play(const QDomNode &node, QPainter *pt)
00791 {
00792 saveAttributes(pt);
00793
00794 ElementType t = (*qSvgTypeMap)[node.nodeName()];
00795
00796 if (t == LineElement && pt->pen().style() == Qt::NoPen) {
00797 QPen p = pt->pen();
00798 p.setStyle(Qt::SolidLine);
00799 pt->setPen(p);
00800 }
00801 QDomNamedNodeMap attr = node.attributes();
00802 if (attr.contains("style"))
00803 setStyle(attr.namedItem("style").nodeValue(), pt);
00804
00805 if (t != SvgElement && attr.contains("transform"))
00806 setTransform(attr.namedItem("transform").nodeValue(), pt);
00807 uint i = attr.length();
00808 if (i > 0) {
00809 QPen pen = pt->pen();
00810 QFont font = pt->font();
00811 while (i--) {
00812 QDomNode n = attr.item(i);
00813 QString a = n.nodeName();
00814 QString val = n.nodeValue().toLower().trimmed();
00815 setStyleProperty(a, val, &pen, &font, &curr->textalign, pt);
00816 }
00817 pt->setPen(pen);
00818 pt->setFont(font);
00819 }
00820
00821 double x1, y1, x2, y2, rx, ry, w, h;
00822 double cx1, cy1, crx, cry;
00823 switch (t) {
00824 case CommentElement:
00825
00826 break;
00827 case RectElement:
00828 rx = ry = 0;
00829 x1 = lenToDouble(attr, "x");
00830 y1 = lenToDouble(attr, "y");
00831 w = lenToDouble(attr, "width");
00832 h = lenToDouble(attr, "height");
00833 if (w == 0 || h == 0)
00834 break;
00835 x2 = attr.contains("rx");
00836 y2 = attr.contains("ry");
00837 if (x2)
00838 rx = lenToDouble(attr, "rx");
00839 if (y2)
00840 ry = lenToDouble(attr, "ry");
00841 if (x2 && !y2)
00842 ry = rx;
00843 else if (!x2 && y2)
00844 rx = ry;
00845 rx = 200.0*rx / w;
00846 ry = 200.0*ry / h;
00847 pt->drawRoundRect(QRectF(x1, y1, w, h), int(rx), int(ry));
00848 break;
00849 case CircleElement:
00850 cx1 = lenToDouble(attr, "cx") + 0.5;
00851 cy1 = lenToDouble(attr, "cy") + 0.5;
00852 crx = lenToDouble(attr, "r");
00853 pt->drawEllipse(QRectF(cx1-crx, cy1-crx, 2*crx, 2*crx));
00854 break;
00855 case EllipseElement:
00856 cx1 = lenToDouble(attr, "cx") + 0.5;
00857 cy1 = lenToDouble(attr, "cy") + 0.5;
00858 crx = lenToDouble(attr, "rx");
00859 cry = lenToDouble(attr, "ry");
00860 pt->drawEllipse(QRectF(cx1-crx, cy1-cry, 2*crx, 2*cry));
00861 break;
00862 case LineElement:
00863 {
00864 x1 = lenToDouble(attr, "x1");
00865 x2 = lenToDouble(attr, "x2");
00866 y1 = lenToDouble(attr, "y1");
00867 y2 = lenToDouble(attr, "y2");
00868 QPen p = pt->pen();
00869 w = p.width();
00870 p.setWidth((unsigned int)(w * (qAbs(pt->worldMatrix().m11()) + qAbs(pt->worldMatrix().m22())) / 2));
00871 pt->setPen(p);
00872 pt->drawLine(QLineF(x1, y1, x2, y2));
00873 p.setWidthF(w);
00874 pt->setPen(p);
00875 }
00876 break;
00877 case PolylineElement:
00878 case PolygonElement:
00879 {
00880 QString pts = attr.namedItem("points").nodeValue();
00881 pts = pts.simplified();
00882 QStringList sl = pts.split(QRegExp(QString::fromLatin1("[,\\s]")),
00883 QString::SkipEmptyParts);
00884 QPolygonF ptarr((uint) sl.count() / 2);
00885 for (int i = 0; i < (int) sl.count() / 2; i++) {
00886 double dx = sl[2*i].toDouble();
00887 double dy = sl[2*i+1].toDouble();
00888 ptarr[i] = QPointF(dx, dy);
00889 }
00890 if (t == PolylineElement) {
00891 if (pt->brush().style() != Qt::NoBrush) {
00892 QPen pn = pt->pen();
00893 pt->setPen(Qt::NoPen);
00894 pt->drawPolygon(ptarr);
00895 pt->setPen(pn);
00896 }
00897 pt->drawPolyline(ptarr);
00898 } else {
00899 pt->drawPolygon(ptarr);
00900 }
00901 }
00902 break;
00903 case SvgElement:
00904 case GroupElement:
00905 case AnchorElement:
00906 {
00907 QDomNode child = node.firstChild();
00908 while (!child.isNull()) {
00909 play(child, pt);
00910 child = child.nextSibling();
00911 }
00912 }
00913 break;
00914 case PathElement:
00915 drawPath(attr.namedItem("d").nodeValue(), pt);
00916 break;
00917 case TSpanElement:
00918 case TextElement:
00919 {
00920 if (attr.contains("x"))
00921 curr->textx = lenToDouble(attr, "x");
00922 if (attr.contains("y"))
00923 curr->texty = lenToDouble(attr, "y");
00924 if (t == TSpanElement) {
00925 curr->textx += lenToDouble(attr, "dx");
00926 curr->texty += lenToDouble(attr, "dy");
00927 }
00928
00929 QPen pn = pt->pen();
00930 QColor pcolor = pn.color();
00931 QColor bcolor = pt->brush().color();
00932 QDomNode c = node.firstChild();
00933 while (!c.isNull()) {
00934 if (c.isText()) {
00935
00936 pn.setColor(bcolor);
00937 pt->setPen(pn);
00938 QString text = c.toText().nodeValue();
00939 text = text.simplified();
00940 w = pt->fontMetrics().width(text);
00941 if (curr->textalign == Qt::AlignHCenter)
00942 curr->textx -= w / 2;
00943 else if (curr->textalign == Qt::AlignRight)
00944 curr->textx -= w;
00945 pt->drawText(QPointF(curr->textx, curr->texty), text);
00946
00947 pn.setColor(pcolor);
00948 pt->setPen(pn);
00949 curr->textx += w;
00950 } else if (c.isElement() && c.toElement().tagName() == "tspan") {
00951 play(c, pt);
00952
00953 }
00954 c = c.nextSibling();
00955 }
00956 if (t == TSpanElement) {
00957
00958 StateList::Iterator it = --(--stack.end());
00959 (*it).textx = curr->textx;
00960 (*it).texty = curr->texty;
00961 }
00962 }
00963 break;
00964 case ImageElement:
00965 {
00966 x1 = lenToDouble(attr, "x");
00967 y1 = lenToDouble(attr, "y");
00968 w = lenToDouble(attr, "width");
00969 h = lenToDouble(attr, "height");
00970 QString href = attr.namedItem("xlink:href").nodeValue();
00971
00972 QPixmap pix;
00973 if (!pix.load(href)){
00974 qWarning("Q3SVGPaintEngine::play: Couldn't load image %s",href.latin1());
00975 break;
00976 }
00977 pt->drawPixmap(QRectF(x1, y1, w, h), pix, QRectF());
00978 }
00979 break;
00980 case DescElement:
00981 case TitleElement:
00982
00983 break;
00984 case ClipElement:
00985 {
00986 restoreAttributes(pt);
00987 QDomNode child = node.firstChild();
00988 QDomNamedNodeMap childAttr = child.attributes();
00989 if (child.nodeName() == "rect") {
00990 QRect r;
00991 r.setX(lenToInt(childAttr, "x"));
00992 r.setY(lenToInt(childAttr, "y"));
00993 r.setWidth(lenToInt(childAttr, "width"));
00994 r.setHeight(lenToInt(childAttr, "height"));
00995 pt->setClipRect(r);
00996 } else if (child.nodeName() == "ellipse") {
00997 QRect r;
00998 int x = lenToInt(childAttr, "cx");
00999 int y = lenToInt(childAttr, "cy");
01000 int width = lenToInt(childAttr, "rx");
01001 int height = lenToInt(childAttr, "ry");
01002 r.setX(x - width);
01003 r.setY(y - height);
01004 r.setWidth(width * 2);
01005 r.setHeight(height * 2);
01006 QRegion rgn(r, QRegion::Ellipse);
01007 pt->setClipRegion(rgn);
01008 }
01009 break;
01010 }
01011 case InvalidElement:
01012 qWarning("Q3SVGPaintEngine::play: unknown element type %s", node.nodeName().latin1());
01013 break;
01014 }
01015
01016 if (t != ClipElement)
01017 restoreAttributes(pt);
01018
01019 return true;
01020 }
01021
01030 double Q3SVGPaintEnginePrivate::parseLen(const QString &str, bool *ok, bool horiz) const
01031 {
01032 QRegExp reg(QString::fromLatin1("([+-]?\\d*\\.*\\d*[Ee]?[+-]?\\d*)(em|ex|px|%|pt|pc|cm|mm|in|)$"));
01033 if (reg.indexIn(str) == -1) {
01034 qWarning("Q3SVGPaintEngine::parseLen: couldn't parse %s", str.latin1());
01035 if (ok)
01036 *ok = false;
01037 return 0.0;
01038 }
01039
01040 double dbl = reg.cap(1).toDouble();
01041 QString u = reg.cap(2);
01042 if (!u.isEmpty() && u != "px") {
01043 if (u == "em") {
01044 QFontInfo fi(cfont);
01045 dbl *= fi.pixelSize();
01046 } else if (u == "ex") {
01047 QFontInfo fi(cfont);
01048 dbl *= 0.5 * fi.pixelSize();
01049 } else if (u == "%")
01050 dbl *= (horiz ? wwidth : wheight)/100.0;
01051 else if (u == "cm")
01052 dbl *= dev->logicalDpiX() / 2.54;
01053 else if (u == "mm")
01054 dbl *= dev->logicalDpiX() / 25.4;
01055 else if (u == "in")
01056 dbl *= dev->logicalDpiX();
01057 else if (u == "pt")
01058 dbl *= dev->logicalDpiX() / 72.0;
01059 else if (u == "pc")
01060 dbl *= dev->logicalDpiX() / 6.0;
01061 else
01062 qWarning("Q3SVGPaintEngine::parseLen: Unknown unit %s", u.latin1());
01063 }
01064 if (ok)
01065 *ok = true;
01066 return dbl;
01067 }
01068
01077 int Q3SVGPaintEnginePrivate::lenToInt(const QDomNamedNodeMap &map, const QString &attr, int def) const
01078 {
01079 if (map.contains(attr)) {
01080 bool ok;
01081 double dbl = parseLen(map.namedItem(attr).nodeValue(), &ok);
01082 if (ok)
01083 return qRound(dbl);
01084 }
01085 return def;
01086 }
01087
01088 double Q3SVGPaintEnginePrivate::lenToDouble(const QDomNamedNodeMap &map, const QString &attr,
01089 int def) const
01090 {
01091 if (map.contains(attr)) {
01092 bool ok;
01093 double x = parseLen(map.namedItem(attr).nodeValue(), &ok);
01094 if (ok) return x;
01095 }
01096 return static_cast<double>(def);
01097 }
01098
01099 void Q3SVGPaintEnginePrivate::setTransform(const QString &tr, QPainter *pt)
01100 {
01101 QString t = tr.simplified();
01102
01103 QRegExp reg(QString::fromLatin1("\\s*([\\w]+)\\s*\\(([^\\(]*)\\)"));
01104 int index = 0;
01105 while ((index = reg.indexIn(t, index)) >= 0) {
01106 QString command = reg.cap(1);
01107 QString params = reg.cap(2);
01108 QStringList plist = params.split(QRegExp(QString::fromLatin1("[,\\s]")),
01109 QString::SkipEmptyParts);
01110 if (command == "translate") {
01111 double tx = 0, ty = 0;
01112 tx = plist[0].toDouble();
01113 if (plist.count() >= 2)
01114 ty = plist[1].toDouble();
01115 pt->translate(tx, ty);
01116 } else if (command == "rotate") {
01117 pt->rotate(plist[0].toDouble());
01118 } else if (command == "scale") {
01119 double sx, sy;
01120 sx = sy = plist[0].toDouble();
01121 if (plist.count() >= 2)
01122 sy = plist[1].toDouble();
01123 pt->scale(sx, sy);
01124 } else if (command == "matrix" && plist.count() >= 6) {
01125 double m[6];
01126 for (int i = 0; i < 6; i++)
01127 m[i] = plist[i].toDouble();
01128 QMatrix wm(m[0], m[1], m[2], m[3], m[4], m[5]);
01129 pt->setWorldMatrix(wm, true);
01130 } else if (command == "skewX") {
01131 pt->shear(0.0, tan(plist[0].toDouble() * deg2rad));
01132 } else if (command == "skewY") {
01133 pt->shear(tan(plist[0].toDouble() * deg2rad), 0.0);
01134 }
01135
01136
01137 index += reg.matchedLength();
01138 }
01139 }
01148 void Q3SVGPaintEnginePrivate::saveAttributes(QPainter *pt)
01149 {
01150 pt->save();
01151
01152 Q3SVGPaintEngineState st(*curr);
01153 stack.append(st);
01154 curr = &stack.last();
01155 }
01156
01165 void Q3SVGPaintEnginePrivate::restoreAttributes(QPainter *pt)
01166 {
01167 pt->restore();
01168 Q_ASSERT(stack.count() > 1);
01169 stack.removeLast();
01170 curr = &stack.last();
01171 }
01172
01173 void Q3SVGPaintEnginePrivate::setStyle(const QString &s, QPainter *pt)
01174 {
01175 QStringList rules = s.split(QChar(';'), QString::SkipEmptyParts);
01176
01177 QPen pen = pt->pen();
01178 QFont font = pt->font();
01179
01180 QStringList::ConstIterator it = rules.constBegin();
01181 for (; it != rules.constEnd(); it++) {
01182 int col = (*it).indexOf(':');
01183 if (col > 0) {
01184 QString prop = (*it).left(col).simplified();
01185 QString val = (*it).right((*it).length() - col - 1);
01186 val = val.toLower().trimmed();
01187 setStyleProperty(prop, val, &pen, &font, &curr->textalign, pt);
01188 }
01189 }
01190 pt->setPen(pen);
01191 pt->setFont(font);
01192 }
01193
01194 void Q3SVGPaintEnginePrivate::setStyleProperty(const QString &prop, const QString &val, QPen *pen,
01195 QFont *font, int *talign, QPainter *pt)
01196 {
01197 if (prop == "stroke") {
01198 if (val == "none") {
01199 pen->setStyle(Qt::NoPen);
01200 } else {
01201 pen->setColor(parseColor(val));
01202 if (pen->style() == Qt::NoPen)
01203 pen->setStyle(Qt::SolidLine);
01204 if (pen->width() == 0)
01205 pen->setWidth(1);
01206 }
01207 } else if (prop == "stroke-opacity") {
01208 double opacity = parseLen(val);
01209 QColor c = pen->color();
01210 c.setAlpha((int)(opacity*255));
01211 pen->setColor(c);
01212 } else if (prop == "fill-opacity") {
01213 double opacity = parseLen(val);
01214 QColor c = pt->brush().color();
01215 c.setAlpha((int)(opacity*255));
01216 pt->setBrush(c);
01217 } else if (prop == "stroke-width") {
01218 double w = parseLen(val);
01219 if (w > 0.0001)
01220 pen->setWidth(int(w));
01221 else
01222 pen->setStyle(Qt::NoPen);
01223 } else if (prop == "stroke-linecap") {
01224 if (val == "butt")
01225 pen->setCapStyle(Qt::FlatCap);
01226 else if (val == "round")
01227 pen->setCapStyle(Qt::RoundCap);
01228 else if (val == "square")
01229 pen->setCapStyle(Qt::SquareCap);
01230 } else if (prop == "stroke-linejoin") {
01231 if (val == "miter")
01232 pen->setJoinStyle(Qt::MiterJoin);
01233 else if (val == "round")
01234 pen->setJoinStyle(Qt::RoundJoin);
01235 else if (val == "bevel")
01236 pen->setJoinStyle(Qt::BevelJoin);
01237 } else if (prop == "stroke-dasharray") {
01238 if (val == "18,6")
01239 pen->setStyle(Qt::DashLine);
01240 else if (val == "3")
01241 pen->setStyle(Qt::DotLine);
01242 else if (val == "9,6,3,6")
01243 pen->setStyle(Qt::DashDotLine);
01244 else if (val == "9,3,3")
01245 pen->setStyle(Qt::DashDotDotLine);
01246 } else if (prop == "fill") {
01247 if (val == "none")
01248 pt->setBrush(Qt::NoBrush);
01249 else
01250 pt->setBrush(parseColor(val));
01251 } else if (prop == "font-size") {
01252 font->setPixelSize(qRound(parseLen(val)));
01253 } else if (prop == "font-family") {
01254 font->setFamily(val);
01255 } else if (prop == "font-style") {
01256 if (val == "normal")
01257 font->setItalic(false);
01258 else if (val == "italic")
01259 font->setItalic(true);
01260 else
01261 qWarning("QSvgDevice::setStyleProperty: unhandled font-style: %s", val.latin1());
01262 } else if (prop == "font-weight") {
01263 int w = font->weight();
01264
01265 if (val == "100" || val == "200")
01266 w = QFont::Light;
01267 if (val == "300" || val == "400" || val == "normal")
01268 w = QFont::Normal;
01269 else if (val == "500" || val == "600")
01270 w = QFont::DemiBold;
01271 else if (val == "700" || val == "bold" || val == "800")
01272 w = QFont::Bold;
01273 else if (val == "900")
01274 w = QFont::Black;
01275 font->setWeight(w);
01276 } else if (prop == "text-anchor") {
01277 if (val == "middle")
01278 *talign = Qt::AlignHCenter;
01279 else if (val == "end")
01280 *talign = Qt::AlignRight;
01281 else
01282 *talign = Qt::AlignLeft;
01283 }
01284 }
01285
01286 void Q3SVGPaintEnginePrivate::drawPath(const QString &data, QPainter *pt)
01287 {
01288 double x0 = 0, y0 = 0;
01289 double x = 0, y = 0;
01290 QPointF ctrlPt;
01291 QPainterPath path;
01292 int idx = 0;
01293 int mode = 0, lastMode = 0;
01294 bool relative = false;
01295 QString commands("MZLHVCSQTA");
01296 int cmdArgs[] = { 2, 0, 2, 1, 1, 6, 4, 4, 2, 7 };
01297 QRegExp reg(QString::fromLatin1("\\s*,?\\s*([+-]?\\d*\\.?\\d*)"));
01298
01299
01300 while (idx < data.length()) {
01301 QChar ch = data[(int)idx++];
01302 if (ch.isSpace())
01303 continue;
01304 QChar chUp = ch.toUpper();
01305 int cmd = commands.indexOf(chUp);
01306 if (cmd >= 0) {
01307
01308 mode = cmd;
01309 relative = (ch != chUp);
01310 } else {
01311 if (mode && !ch.isLetter()) {
01312 cmd = mode;
01313 idx--;
01314 } else {
01315 qWarning("Q3SVGPaintEngine::drawPath: Unknown command");
01316 return;
01317 }
01318 }
01319
01320
01321 const int maxArgs = 7;
01322 double arg[maxArgs];
01323 int numArgs = cmdArgs[cmd];
01324 for (int i = 0; i < numArgs; i++) {
01325 int pos = reg.indexIn(data, idx);
01326 if (pos == -1) {
01327 qWarning("Q3SVGPaintEngine::drawPath: Error parsing arguments");
01328 return;
01329 }
01330 arg[i] = reg.cap(1).toDouble();
01331 idx = pos + reg.matchedLength();
01332 };
01333
01334
01335 double offsetX = relative ? x : 0;
01336 double offsetY = relative ? y : 0;
01337 switch (mode) {
01338 case 0:
01339 x = x0 = arg[0] + offsetX;
01340 y = y0 = arg[1] + offsetY;
01341 path.moveTo(x0, y0);
01342 mode = 2;
01343 break;
01344 case 1:
01345 x = x0;
01346 y = y0;
01347 path.closeSubpath();
01348 mode = 0;
01349 break;
01350 case 2:
01351 x = arg[0] + offsetX;
01352 y = arg[1] + offsetY;
01353 path.lineTo(x, y);
01354 break;
01355 case 3:
01356 x = arg[0] + offsetX;
01357 path.lineTo(x, y);
01358 break;
01359 case 4:
01360 y = arg[0] + offsetY;
01361 path.lineTo(x, y);
01362 break;
01363 case 5: {
01364 QPointF c1(arg[0]+offsetX, arg[1]+offsetY);
01365 QPointF c2(arg[2]+offsetX, arg[3]+offsetY);
01366 QPointF e(arg[4]+offsetX, arg[5]+offsetY);
01367 path.cubicTo(c1, c2, e);
01368 ctrlPt = c2;
01369 x = e.x();
01370 y = e.y();
01371 break;
01372 }
01373 case 6: {
01374 QPointF c1;
01375 if (lastMode == 5 || lastMode == 6)
01376 c1 = QPointF(2*x-ctrlPt.x(), 2*y-ctrlPt.y());
01377 else
01378 c1 = QPointF(x, y);
01379 QPointF c2(arg[0]+offsetX, arg[1]+offsetY);
01380 QPointF e(arg[2]+offsetX, arg[3]+offsetY);
01381 path.cubicTo(c1, c2, e);
01382 ctrlPt = c2;
01383 x = e.x();
01384 y = e.y();
01385 break;
01386 }
01387 case 7: {
01388 QPointF c(arg[0]+offsetX, arg[1]+offsetY);
01389 QPointF e(arg[2]+offsetX, arg[3]+offsetY);
01390 path.quadTo(c, e);
01391 ctrlPt = c;
01392 x = e.x();
01393 y = e.y();
01394 break;
01395 }
01396 case 8: {
01397 QPointF e(arg[0]+offsetX, arg[1]+offsetY);
01398 QPointF c;
01399 if (lastMode == 7 || lastMode == 8)
01400 c = QPointF(2*x-ctrlPt.x(), 2*y-ctrlPt.y());
01401 else
01402 c = QPointF(x, y);
01403 path.quadTo(c, e);
01404 ctrlPt = c;
01405 x = e.x();
01406 y = e.y();
01407 break;
01408 }
01409 case 9:
01410
01411 x = arg[5] + offsetX;
01412 y = arg[6] + offsetY;
01413 path.lineTo(x, y);
01414 break;
01415 };
01416 lastMode = mode;
01417 }
01418 pt->drawPath(path);
01419 }
01420
01428 QColor Q3SVGPaintEnginePrivate::parseColor(const QString &col)
01429 {
01430 static const struct ColorTable {
01431 const char *name;
01432 const char *rgb;
01433 } coltab[] = {
01434 { "black", "#000000" },
01435 { "silver", "#c0c0c0" },
01436 { "gray", "#808080" },
01437 { "white", "#ffffff" },
01438 { "maroon", "#800000" },
01439 { "red", "#ff0000" },
01440 { "purple", "#800080" },
01441 { "fuchsia", "#ff00ff" },
01442 { "green", "#008000" },
01443 { "lime", "#00ff00" },
01444 { "olive", "#808000" },
01445 { "yellow", "#ffff00" },
01446 { "navy", "#000080" },
01447 { "blue", "#0000ff" },
01448 { "teal", "#008080" },
01449 { "aqua", "#00ffff" },
01450
01451 { 0, 0 }
01452 };
01453
01454
01455 if (!qSvgColMap) {
01456 qSvgColMap = new QMap<QString,QString>;
01457 const struct ColorTable *t = coltab;
01458 while (t->name) {
01459 qSvgColMap->insert(t->name, t->rgb);
01460 ++t;
01461 }
01462 }
01463
01464
01465 if (qSvgColMap->contains(col))
01466 return QColor((*qSvgColMap)[col]);
01467
01468 QString c = col;
01469 c.replace(QRegExp(QString::fromLatin1("\\s*")), "");
01470 QRegExp reg(QString::fromLatin1("^rgb\\((\\d+)(%?),(\\d+)(%?),(\\d+)(%?)\\)$"));
01471 if (reg.indexIn(c) >= 0) {
01472 int comp[3];
01473 for (int i = 0; i < 3; i++) {
01474 comp[i] = reg.cap(2*i+1).toInt();
01475 if (!reg.cap(2*i+2).isEmpty())
01476 comp[i] = int((double(255*comp[i])/100.0));
01477 }
01478 return QColor(comp[0], comp[1], comp[2]);
01479 }
01480
01481
01482 return QColor(col);
01483 }
01484
01485 static QString qt_svg_compose_path(const QPainterPath &path)
01486 {
01487 QString str, tmp;
01488 for (int i = 0; i < path.elementCount(); ++i) {
01489 const QPainterPath::Element &elm = path.elementAt(i);
01490 switch (elm.type) {
01491 case QPainterPath::LineToElement:
01492 tmp.sprintf("L %f %f ", elm.x, elm.y);
01493 str += tmp;
01494 break;
01495 case QPainterPath::MoveToElement:
01496 tmp.sprintf("M %f %f ", elm.x, elm.y);
01497 str += tmp;
01498 break;
01499 case QPainterPath::CurveToElement:
01500 {
01501 Q_ASSERT(path.elementCount() > i+2);
01502 const QPainterPath::Element cd1 = path.elementAt(i+1);
01503 const QPainterPath::Element cd2 = path.elementAt(i+2);
01504 Q_ASSERT(cd1.type == QPainterPath::CurveToDataElement
01505 && cd2.type == QPainterPath::CurveToDataElement);
01506 tmp.sprintf("C %f %f %f %f %f %f ", elm.x, elm.y, cd1.x, cd1.y, cd2.x, cd2.y);
01507 str += tmp;
01508 i += 2;
01509 break;
01510 }
01511 default:
01512 break;
01513 }
01514 }
01515 return str;
01516 }
01517