📄 q3paintengine_svg.cpp
字号:
{"ellipse", EllipseElement }, {"g", GroupElement }, {"image", ImageElement }, {"line", LineElement }, {"polyline", PolylineElement}, {"polygon", PolygonElement }, {"path", PathElement }, {"rect", RectElement }, {"svg", SvgElement }, {"text", TextElement }, {"tspan", TSpanElement }, {"title", TitleElement }, {0, InvalidElement } }; // initialize only once if (!qSvgTypeMap) { qSvgTypeMap = new QSvgTypeMap; const ElementTable *t = etab; while (t->name) { qSvgTypeMap->insert(QLatin1String(t->name), t->type); t++; } } // initial state Q3SVGPaintEngineState st; st.textx = st.texty = 0; st.textalign = Qt::AlignLeft; d->stack.append(st); d->curr = &d->stack.last(); // 'play' all elements recursively starting with 'svg' as root bool b = d->play(svg, pt); d->stack.removeFirst(); return b;}bool Q3SVGPaintEnginePrivate::play(const QDomNode &node, QPainter *pt){ saveAttributes(pt); ElementType t = (*qSvgTypeMap)[node.nodeName()]; if (t == LineElement && pt->pen().style() == Qt::NoPen) { QPen p = pt->pen(); p.setStyle(Qt::SolidLine); pt->setPen(p); } QDomNamedNodeMap attr = node.attributes(); if (attr.contains(QLatin1String("style"))) setStyle(attr.namedItem(QLatin1String("style")).nodeValue(), pt); // ### might have to exclude more elements from transform if (t != SvgElement && attr.contains(QLatin1String("transform"))) setTransform(attr.namedItem(QLatin1String("transform")).nodeValue(), pt); uint i = attr.length(); if (i > 0) { QPen pen = pt->pen(); QFont font = pt->font(); while (i--) { QDomNode n = attr.item(i); QString a = n.nodeName(); QString val = n.nodeValue().toLower().trimmed(); setStyleProperty(a, val, &pen, &font, &curr->textalign, pt); } pt->setPen(pen); pt->setFont(font); } double x1, y1, x2, y2, rx, ry, w, h; double cx1, cy1, crx, cry; switch (t) { case CommentElement: // ignore break; case RectElement: rx = ry = 0; x1 = lenToDouble(attr, QLatin1String("x")); y1 = lenToDouble(attr, QLatin1String("y")); w = lenToDouble(attr, QLatin1String("width")); h = lenToDouble(attr, QLatin1String("height")); if (w == 0 || h == 0) // prevent div by zero below break; x2 = attr.contains(QLatin1String("rx")); // tiny abuse of x2 and y2 y2 = attr.contains(QLatin1String("ry")); if (x2) rx = lenToDouble(attr, QLatin1String("rx")); if (y2) ry = lenToDouble(attr, QLatin1String("ry")); if (x2 && !y2) ry = rx; else if (!x2 && y2) rx = ry; rx = 200.0*rx / w; ry = 200.0*ry / h; pt->drawRoundRect(QRectF(x1, y1, w, h), int(rx), int(ry)); break; case CircleElement: cx1 = lenToDouble(attr, QLatin1String("cx")) + 0.5; cy1 = lenToDouble(attr, QLatin1String("cy")) + 0.5; crx = lenToDouble(attr, QLatin1String("r")); pt->drawEllipse(QRectF(cx1-crx, cy1-crx, 2*crx, 2*crx)); break; case EllipseElement: cx1 = lenToDouble(attr, QLatin1String("cx")) + 0.5; cy1 = lenToDouble(attr, QLatin1String("cy")) + 0.5; crx = lenToDouble(attr, QLatin1String("rx")); cry = lenToDouble(attr, QLatin1String("ry")); pt->drawEllipse(QRectF(cx1-crx, cy1-cry, 2*crx, 2*cry)); break; case LineElement: { x1 = lenToDouble(attr, QLatin1String("x1")); x2 = lenToDouble(attr, QLatin1String("x2")); y1 = lenToDouble(attr, QLatin1String("y1")); y2 = lenToDouble(attr, QLatin1String("y2")); QPen p = pt->pen(); w = p.width(); p.setWidth((unsigned int)(w * (qAbs(pt->worldMatrix().m11()) + qAbs(pt->worldMatrix().m22())) / 2)); pt->setPen(p); pt->drawLine(QLineF(x1, y1, x2, y2)); p.setWidthF(w); pt->setPen(p); } break; case PolylineElement: case PolygonElement: { QString pts = attr.namedItem(QLatin1String("points")).nodeValue(); pts = pts.simplified(); QStringList sl = pts.split(QRegExp(QString::fromLatin1("[,\\s]")), QString::SkipEmptyParts); QPolygonF ptarr((uint) sl.count() / 2); for (int i = 0; i < (int) sl.count() / 2; i++) { double dx = sl[2*i].toDouble(); double dy = sl[2*i+1].toDouble(); ptarr[i] = QPointF(dx, dy); } if (t == PolylineElement) { if (pt->brush().style() != Qt::NoBrush) { QPen pn = pt->pen(); pt->setPen(Qt::NoPen); pt->drawPolygon(ptarr); pt->setPen(pn); } pt->drawPolyline(ptarr); // ### closes when filled. bug ? } else { pt->drawPolygon(ptarr); } } break; case SvgElement: case GroupElement: case AnchorElement: { QDomNode child = node.firstChild(); while (!child.isNull()) { play(child, pt); child = child.nextSibling(); } } break; case PathElement: drawPath(attr.namedItem(QLatin1String("d")).nodeValue(), pt); break; case TSpanElement: case TextElement: { if (attr.contains(QLatin1String("x"))) curr->textx = lenToDouble(attr, QLatin1String("x")); if (attr.contains(QLatin1String("y"))) curr->texty = lenToDouble(attr, QLatin1String("y")); if (t == TSpanElement) { curr->textx += lenToDouble(attr, QLatin1String("dx")); curr->texty += lenToDouble(attr, QLatin1String("dy")); } // backup old colors QPen pn = pt->pen(); QColor pcolor = pn.color(); QColor bcolor = pt->brush().color(); QDomNode c = node.firstChild(); while (!c.isNull()) { if (c.isText()) { // we have pen and brush reversed for text drawing pn.setColor(bcolor); pt->setPen(pn); QString text = c.toText().nodeValue(); text = text.simplified(); // ### 'preserve' w = pt->fontMetrics().width(text); if (curr->textalign == Qt::AlignHCenter) curr->textx -= w / 2; else if (curr->textalign == Qt::AlignRight) curr->textx -= w; pt->drawText(QPointF(curr->textx, curr->texty), text); // restore pen pn.setColor(pcolor); pt->setPen(pn); curr->textx += w; } else if (c.isElement() && c.toElement().tagName() == QLatin1String("tspan")) { play(c, pt); } c = c.nextSibling(); } if (t == TSpanElement) { // move current text position in parent text element StateList::Iterator it = --(--stack.end()); (*it).textx = curr->textx; (*it).texty = curr->texty; } } break; case ImageElement: { x1 = lenToDouble(attr, QLatin1String("x")); y1 = lenToDouble(attr, QLatin1String("y")); w = lenToDouble(attr, QLatin1String("width")); h = lenToDouble(attr, QLatin1String("height")); QString href = attr.namedItem(QLatin1String("xlink:href")).nodeValue(); // ### catch references to embedded .svg files QPixmap pix; if (!pix.load(href)){ qWarning("Q3SVGPaintEngine::play: Couldn't load image %s",href.latin1()); break; } pt->drawPixmap(QRectF(x1, y1, w, h), pix, QRectF()); } break; case DescElement: case TitleElement: // ignored for now break; case ClipElement: { restoreAttributes(pt); // To ensure the clip rect is saved, we need to restore now QDomNode child = node.firstChild(); QDomNamedNodeMap childAttr = child.attributes(); if (child.nodeName() == QLatin1String("rect")) { QRect r; r.setX(lenToInt(childAttr, QLatin1String("x"))); r.setY(lenToInt(childAttr, QLatin1String("y"))); r.setWidth(lenToInt(childAttr, QLatin1String("width"))); r.setHeight(lenToInt(childAttr, QLatin1String("height"))); pt->setClipRect(r); } else if (child.nodeName() == QLatin1String("ellipse")) { QRect r; int x = lenToInt(childAttr, QLatin1String("cx")); int y = lenToInt(childAttr, QLatin1String("cy")); int width = lenToInt(childAttr, QLatin1String("rx")); int height = lenToInt(childAttr, QLatin1String("ry")); r.setX(x - width); r.setY(y - height); r.setWidth(width * 2); r.setHeight(height * 2); QRegion rgn(r, QRegion::Ellipse); pt->setClipRegion(rgn); } break; } case InvalidElement: qWarning("Q3SVGPaintEngine::play: unknown element type %s", node.nodeName().latin1()); break; } if (t != ClipElement) restoreAttributes(pt); return true;}/*! \internal Parse a <length> datatype consisting of a number followed by an optional unit specifier. Can be used for type <coordinate> as well. For relative units the value of \a horiz will determine whether the horizontal or vertical dimension will be used.*/double Q3SVGPaintEnginePrivate::parseLen(const QString &str, bool *ok, bool horiz) const{ QRegExp reg(QString::fromLatin1("([+-]?\\d*\\.*\\d*[Ee]?[+-]?\\d*)(em|ex|px|%|pt|pc|cm|mm|in|)$")); if (reg.indexIn(str) == -1) { qWarning("Q3SVGPaintEngine::parseLen: couldn't parse %s", str.latin1()); if (ok) *ok = false; return 0.0; } double dbl = reg.cap(1).toDouble(); QString u = reg.cap(2); if (!u.isEmpty() && u != QLatin1String("px")) { if (u == QLatin1String("em")) { QFontInfo fi(cfont); dbl *= fi.pixelSize(); } else if (u == QLatin1String("ex")) { QFontInfo fi(cfont); dbl *= 0.5 * fi.pixelSize(); } else if (u == QLatin1String("%")) dbl *= (horiz ? wwidth : wheight)/100.0; else if (u == QLatin1String("cm")) dbl *= dev->logicalDpiX() / 2.54; else if (u == QLatin1String("mm")) dbl *= dev->logicalDpiX() / 25.4; else if (u == QLatin1String("in")) dbl *= dev->logicalDpiX(); else if (u == QLatin1String("pt")) dbl *= dev->logicalDpiX() / 72.0; else if (u == QLatin1String("pc")) dbl *= dev->logicalDpiX() / 6.0; else qWarning("Q3SVGPaintEngine::parseLen: Unknown unit %s", u.latin1()); } if (ok) *ok = true; return dbl;}/*! \internal Returns the length specified in attribute \a attr in \a map. If the specified attribute doesn't exist or can't be parsed \a def is returned.*/int Q3SVGPaintEnginePrivate::lenToInt(const QDomNamedNodeMap &map, const QString &attr, int def) const{ if (map.contains(attr)) { bool ok; double dbl = parseLen(map.namedItem(attr).nodeValue(), &ok); if (ok) return qRound(dbl); } return def;}double Q3SVGPaintEnginePrivate::lenToDouble(const QDomNamedNodeMap &map, const QString &attr, int def) const{ if (map.contains(attr)) { bool ok; double x = parseLen(map.namedItem(attr).nodeValue(), &ok); if (ok) return x; } return static_cast<double>(def);}void Q3SVGPaintEnginePrivate::setTransform(const QString &tr, QPainter *pt){ QString t = tr.simplified(); QRegExp reg(QString::fromLatin1("\\s*([\\w]+)\\s*\\(([^\\(]*)\\)")); int index = 0; while ((index = reg.indexIn(t, index)) >= 0) { QString command = reg.cap(1); QString params = reg.cap(2); QStringList plist = params.split(QRegExp(QString::fromLatin1("[,\\s]")), QString::SkipEmptyParts); if (command == QLatin1String("translate")) { double tx = 0, ty = 0; tx = plist[0].toDouble(); if (plist.count() >= 2) ty = plist[1].toDouble(); pt->translate(tx, ty); } else if (command == QLatin1String("rotate")) { pt->rotate(plist[0].toDouble()); } else if (command == QLatin1String("scale")) { double sx, sy; sx = sy = plist[0].toDouble(); if (plist.count() >= 2) sy = plist[1].toDouble(); pt->scale(sx, sy); } else if (command == QLatin1String("matrix") && plist.count() >= 6) { double m[6]; for (int i = 0; i < 6; i++) m[i] = plist[i].toDouble(); QMatrix wm(m[0], m[1], m[2], m[3], m[4], m[5]); pt->setWorldMatrix(wm, true); } else if (command == QLatin1String("skewX")) { pt->shear(0.0, tan(plist[0].toDouble() * deg2rad)); } else if (command == QLatin1String("skewY")) { pt->shear(tan(plist[0].toDouble() * deg2rad), 0.0); } // move on to next command
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -