src/qt3support/painting/q3paintengine_svg.cpp

Go to the documentation of this file.
00001 /****************************************************************************
00002 **
00003 ** Copyright (C) 1992-2006 Trolltech ASA. All rights reserved.
00004 **
00005 ** This file is part of the Qt3Support module of the Qt Toolkit.
00006 **
00007 ** This file may be used under the terms of the GNU General Public
00008 ** License version 2.0 as published by the Free Software Foundation
00009 ** and appearing in the file LICENSE.GPL included in the packaging of
00010 ** this file.  Please review the following information to ensure GNU
00011 ** General Public Licensing requirements will be met:
00012 ** http://www.trolltech.com/products/qt/opensource.html
00013 **
00014 ** If you are unsure which license is appropriate for your use, please
00015 ** review the following information:
00016 ** http://www.trolltech.com/products/qt/licensing.html or contact the
00017 ** sales department at sales@trolltech.com.
00018 **
00019 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
00020 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
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;        // pi/180
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; // current text position
00059     int textalign; // text alignment
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; // element types
00090 static QMap<QString,QString> *qSvgColMap=0; // recognized color keyword names
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; // bounding rectangle
00131     QDomDocument doc; // document tree
00132     QDomNode current;
00133 
00134     ImageList images; // old private
00135     PixmapList pixmaps;
00136     StateList stack;
00137     int currentClip;
00138 
00139 //     QPoint curPt;
00140     Q3SVGPaintEngineState *curr;
00141 //     QPainter *pt; // only used by recursive play() et al
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;        // static
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 //     d->wwidth = ps->ww;
00234 //     d->wheight = ps->wh;
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 & /* sr */)
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     // saving to disk and setting the xlink:href attribute will be
00393     // done later in save() once we now the svg document name.
00394     d->appendChild(e, QPicturePrivate::PdcDrawPixmap);
00395 }
00396 
00397 void Q3SVGPaintEngine::drawTiledPixmap(const QRectF & /* r */, const QPixmap & /* pixmap */,
00398                                       const QPointF & /* s */)
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 //     int x, y;
00407 //     const QRect r(p.x(), p.y(), ti.width, ti.ascent + ti.descent);
00408     // horizontal text alignment
00409  //    if ((ti.flags & Qt::AlignHCenter) != 0) {
00410 //         x = r.x() + r.width() / 2;
00411 //         e.setAttribute("text-anchor", "middle");
00412 //     } else if ((textflags & Qt::AlignRight) != 0) {
00413 //         x = r.right();
00414 //         e.setAttribute("text-anchor", "end");
00415 //     } else {
00416 //         x = r.x();
00417 //     }
00418 //     // vertical text alignment
00419 //     if ((textflags & Qt::AlignVCenter) != 0)
00420 //         y = r.y() + (r.height() + ti.ascent) / 2;
00421 //     else if ((textflags & Qt::AlignBottom) != 0)
00422 //         y = r.bottom();
00423 //     else
00424 //         y = r.y() + ti.ascent;
00425 //     if (x)
00426 //         e.setAttribute("x", x);
00427 //     if (y)
00428 //         e.setAttribute("y", y);
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     // saving to disk and setting the xlink:href attribute will be
00448     // done later in save() once we now the svg document name.
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     // guess svg id from fileName
00474     QString svgName = fileName.endsWith(".svg") ?
00475                       fileName.left(fileName.length()-4) : fileName;
00476 
00477     // now we have the info about name and dimensions available
00478     QDomElement root = d->doc.documentElement();
00479     root.setAttribute("id", svgName);
00480     // the standard doesn't take respect x and y. But we want a
00481     // proper bounding rect. We make width and height bigger when
00482     // writing out and subtract x and y when reading in.
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     // ... and know how to name any image files to be written out
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         // ### optimize application of attributes utilizing <g>
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)                // only reset when entering
00584                 applyStyle(&e, c);        // or leaving a <g> tag
00585             if (dirtyTransform && e.tagName() != "g") {
00586                 // same as above but not for <g> tags
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     // ### do not write every attribute each time
00598     QColor pcol = cpen.color();
00599     QColor bcol = cbrush.color();
00600     QString s;
00601     if (c == QPicturePrivate::PdcDrawText2 || c == QPicturePrivate::PdcDrawText2Formatted) {
00602         // QPainter has a reversed understanding of pen/stroke vs.
00603         // brush/fill for text
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         // not a very scientific distribution
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;"; // Qt polylines use no brush, neither do Beziers
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); // SVG default pen and brush
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     // force transform to be activated in case our sequences
00704     // are replayed later with a transformed painter
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     // SVG doesn't respect x and y. But we want a proper bounding rect.
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     // initialize only once
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     // initial state
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     // 'play' all elements recursively starting with 'svg' as root
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     // ### might have to exclude more elements from transform
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         // ignore
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) // prevent div by zero below
00834             break;
00835         x2 = attr.contains("rx"); // tiny abuse of x2 and y2
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); // ### closes when filled. bug ?
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             // backup old colors
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                     // we have pen and brush reversed for text drawing
00936                     pn.setColor(bcolor);
00937                     pt->setPen(pn);
00938                     QString text = c.toText().nodeValue();
00939                     text = text.simplified(); // ### 'preserve'
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                     // restore pen
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                 // move current text position in parent text element
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             // ### catch references to embedded .svg files
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         // ignored for now
00983         break;
00984     case ClipElement:
00985         {
00986             restoreAttributes(pt); // To ensure the clip rect is saved, we need to restore now
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         // move on to next command
01137         index += reg.matchedLength();
01138     }
01139 }
01148 void Q3SVGPaintEnginePrivate::saveAttributes(QPainter *pt)
01149 {
01150     pt->save();
01151     // copy old state
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         // no exact equivalents so we have to "round" a little bit
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;              // starting point
01289     double x = 0, y = 0;                // current point
01290     QPointF ctrlPt;
01291     QPainterPath path;                  // resulting path
01292     int idx = 0;                        // current data position
01293     int mode = 0, lastMode = 0;         // parser state
01294     bool relative = false;              // e.g. 'h' vs. 'H'
01295     QString commands("MZLHVCSQTA");     // recognized commands
01296     int cmdArgs[] = { 2, 0, 2, 1, 1, 6, 4, 4, 2, 7 };                   // no of arguments
01297     QRegExp reg(QString::fromLatin1("\\s*,?\\s*([+-]?\\d*\\.?\\d*)"));  // floating point
01298 
01299     // detect next command
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             // switch to new command mode
01308             mode = cmd;
01309             relative = (ch != chUp);                // e.g. 'm' instead of 'M'
01310         } else {
01311             if (mode && !ch.isLetter()) {
01312                 cmd = mode;                        // continue in previous mode
01313                 idx--;
01314             } else {
01315                 qWarning("Q3SVGPaintEngine::drawPath: Unknown command");
01316                 return;
01317             }
01318         }
01319 
01320         // read in the required number of arguments
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         // process command
01335         double offsetX = relative ? x : 0;        // correction offsets
01336         double offsetY = relative ? y : 0;        // for relative commands
01337         switch (mode) {
01338         case 0:                                        // 'M' move to
01339             x = x0 = arg[0] + offsetX;
01340             y = y0 = arg[1] + offsetY;
01341             path.moveTo(x0, y0);
01342             mode = 2;                                  // -> 'L'
01343             break;
01344         case 1:                                        // 'Z' close path
01345             x = x0;
01346             y = y0;
01347             path.closeSubpath();
01348             mode = 0;
01349             break;
01350         case 2:                                        // 'L' line to
01351             x = arg[0] + offsetX;
01352             y = arg[1] + offsetY;
01353             path.lineTo(x, y);
01354             break;
01355         case 3:                                        // 'H' horizontal line
01356             x = arg[0] + offsetX;
01357             path.lineTo(x, y);
01358             break;
01359         case 4:                                        // 'V' vertical line
01360             y = arg[0] + offsetY;
01361             path.lineTo(x, y);
01362             break;
01363         case 5: {                                        // 'C' cubic bezier curveto
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: {                                        // 'S' smooth shorthand
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: {                                        // 'Q' quadratic bezier curves
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: {                                      // 'T' smooth shorthand
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:                                        // 'A' elliptical arc curve
01410             // ### just a straight line
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         // ### the latest spec has more
01451         { 0,         0         }
01452     };
01453 
01454     // initialize color map on first use
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     // a keyword?
01465     if (qSvgColMap->contains(col))
01466         return QColor((*qSvgColMap)[col]);
01467     // in rgb(r,g,b) form ?
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())                // percentage ?
01476                 comp[i] = int((double(255*comp[i])/100.0));
01477         }
01478         return QColor(comp[0], comp[1], comp[2]);
01479     }
01480 
01481     // check for predefined Qt color objects, #RRGGBB and #RGB
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 

Generated on Thu Mar 15 11:58:22 2007 for Qt 4.2 User's Guide by  doxygen 1.5.1