Warning, file /office/calligra/filters/karbon/wmf/WmfImportParser.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /* This file is part of the KDE project
0002  * SPDX-FileCopyrightText: 2003 thierry lorthiois <lorthioist@wanadoo.fr>
0003  * SPDX-FileCopyrightText: 2007-2011 Jan Hambrecht <jaham@gmx.net>
0004  *
0005  * SPDX-License-Identifier: LGPL-2.0-only
0006 */
0007 
0008 #include "WmfImportParser.h"
0009 
0010 #include "WmfImportDebug.h"
0011 
0012 #include <WmfEnums.h>
0013 #include <WmfDeviceContext.h>
0014 
0015 #include <KoXmlWriter.h>
0016 #include <KoUnit.h>
0017 
0018 #include <QBuffer>
0019 
0020 #include <math.h>
0021 
0022 /*
0023 bug : see motar.wmf
0024 */
0025 
0026 #define DEG2RAD(angle) angle * M_PI / 180.0
0027 #define RAD2DEG(angle) angle / M_PI * 180.0
0028 
0029 WMFImportParser::WMFImportParser(KoXmlWriter &svgWriter)
0030     : WmfAbstractBackend(), m_svgWriter(svgWriter)
0031 {
0032 }
0033 
0034 WMFImportParser::~WMFImportParser()
0035 {
0036 }
0037 
0038 //-----------------------------------------------------------------------------
0039 // Virtual Painter
0040 
0041 bool WMFImportParser::begin(const QRect &boundingBox)
0042 {
0043     m_scaleX = m_scaleY = 1;
0044 
0045     m_pageSize = boundingBox.size();
0046 
0047     if (!isStandard()) {
0048         // Placeable Wmf store the boundingRect() in pixel and the default DPI
0049         m_pageSize.rwidth() = INCH_TO_POINT((double)boundingBox.width() / defaultDpi());
0050         m_pageSize.rheight() = INCH_TO_POINT((double)boundingBox.height() / defaultDpi());
0051     }
0052     if ((boundingBox.width() != 0) && (boundingBox.height() != 0)) {
0053         m_scaleX = m_pageSize.width() / (double)boundingBox.width();
0054         m_scaleY = m_pageSize.height() / (double)boundingBox.height();
0055     }
0056 
0057     // standard header:
0058     m_svgWriter.addCompleteElement("<?xml version=\"1.0\" standalone=\"no\"?>\n");
0059     m_svgWriter.addCompleteElement("<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 20010904//EN\" " \
0060                                    "\"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\">\n");
0061 
0062     // add some PR.  one line is more than enough.
0063     m_svgWriter.addCompleteElement("<!-- Created using Karbon, part of Calligra: http://www.calligra.org/karbon -->\n");
0064     m_svgWriter.startElement("svg");
0065     m_svgWriter.addAttribute("xmlns", "http://www.w3.org/2000/svg");
0066     m_svgWriter.addAttribute("xmlns:xlink", "http://www.w3.org/1999/xlink");
0067     m_svgWriter.addAttribute("width", m_pageSize.width());
0068     m_svgWriter.addAttribute("height", m_pageSize.height());
0069 
0070     debugWmf << "bounding rect =" << boundingBox;
0071     debugWmf << "page size =" << m_pageSize;
0072     debugWmf << "scale x =" << m_scaleX;
0073     debugWmf << "scale y =" << m_scaleY;
0074 
0075     m_window.org = boundingBox.topLeft();
0076     //m_viewport.org = boundingBox.topLeft();
0077     m_window.ext = boundingBox.size();
0078     m_window.extIsValid = true;
0079     m_viewport.ext = m_pageSize;
0080     m_viewport.extIsValid = true;
0081 
0082     updateTransform();
0083 
0084     return true;
0085 }
0086 
0087 bool WMFImportParser::end()
0088 {
0089     m_svgWriter.endElement(); // svg
0090     return true;
0091 }
0092 
0093 void WMFImportParser::save()
0094 {
0095 }
0096 
0097 void WMFImportParser::restore()
0098 {
0099 }
0100 
0101 void WMFImportParser::setWindowOrg(int left, int top)
0102 {
0103     debugWmf << left << top;
0104     if (QPoint(left, top) != m_window.org) {
0105         m_window.org.setX(left);
0106         m_window.org.setY(top);
0107         updateTransform();
0108     }
0109 }
0110 
0111 void WMFImportParser::setWindowExt(int width, int height)
0112 {
0113     debugWmf << width << height;
0114     // the wmf file can change width/height during the drawing
0115     if (QSize(width, height) != m_window.ext) {
0116         m_window.ext = QSizeF(width, height);
0117         m_window.extIsValid = true;
0118         updateTransform();
0119     }
0120 }
0121 
0122 void WMFImportParser::setViewportOrg(int left, int top)
0123 {
0124     debugWmf << left << top;
0125     if (QPoint(left, top) != m_viewport.org) {
0126         m_viewport.org.setX(left);
0127         m_viewport.org.setY(top);
0128         updateTransform();
0129     }
0130 }
0131 
0132 void WMFImportParser::setViewportExt(int width, int height)
0133 {
0134     debugWmf << width << height;
0135     if ((width != 0) && (height != 0)) {
0136         m_viewport.ext = QSizeF(width, height);
0137         m_viewport.extIsValid = true;
0138         updateTransform();
0139     }
0140 }
0141 
0142 void WMFImportParser::setMatrix(Libwmf::WmfDeviceContext &/*context*/, const QMatrix &matrix,
0143                                 bool combine)
0144 {
0145     if (combine)
0146         m_matrix = matrix * m_matrix;
0147     else
0148         m_matrix = matrix;
0149 
0150     debugWmf << "matrix =" << matrix;
0151     debugWmf << "combine =" << combine;
0152 }
0153 
0154 void WMFImportParser::setPixel(Libwmf::WmfDeviceContext &context, int x, int y, const QColor &color)
0155 {
0156     Q_UNUSED(context);
0157     Q_UNUSED(x);
0158     Q_UNUSED(y);
0159     Q_UNUSED(color);
0160 
0161     // Not Yet Implemented
0162 }
0163 
0164 void WMFImportParser::lineTo(Libwmf::WmfDeviceContext &context, int left, int top)
0165 {
0166     const QString strokeStyle = saveStroke(context);
0167 
0168     static int lineIndex = 0;
0169 
0170     const QPointF p1 = coord(context.currentPosition);
0171     const QPointF p2 = coord(QPoint(left, top));
0172 
0173     m_svgWriter.startElement("line");
0174     m_svgWriter.addAttribute("id", QString("line%1").arg(++lineIndex));
0175     m_svgWriter.addAttribute("x1", p1.x());
0176     m_svgWriter.addAttribute("y1", p1.y());
0177     m_svgWriter.addAttribute("x2", p2.x());
0178     m_svgWriter.addAttribute("y2", p2.y());
0179     m_svgWriter.addAttribute("style", strokeStyle+"fill:none");
0180     m_svgWriter.endElement(); // line
0181 
0182     context.currentPosition = QPoint(left, top);
0183 }
0184 
0185 void WMFImportParser::drawRect(Libwmf::WmfDeviceContext &context, int left, int top, int width, int height)
0186 {
0187     QRectF bound = boundBox(left, top, width, height);
0188 
0189     const QString fillStyle = saveFill(context);
0190     const QString strokeStyle = saveStroke(context);
0191 
0192     static int rectIndex = 0;
0193 
0194     m_svgWriter.startElement("rect");
0195     m_svgWriter.addAttribute("id", QString("rect%1").arg(++rectIndex));
0196     m_svgWriter.addAttribute("x", bound.x());
0197     m_svgWriter.addAttribute("y", bound.y());
0198     m_svgWriter.addAttribute("width", bound.width());
0199     m_svgWriter.addAttribute("height", bound.height());
0200     m_svgWriter.addAttribute("style", strokeStyle+fillStyle);
0201     m_svgWriter.endElement(); // rect
0202 }
0203 
0204 void WMFImportParser::drawRoundRect(Libwmf::WmfDeviceContext &context, int left, int top, int width, int height, int roundw, int roundh)
0205 {
0206     QRectF bound = boundBox(left, top, width, height);
0207     // roundw and roundh are in percent of width and height
0208     const qreal rx = qAbs(roundw)/100. * bound.width();
0209     const qreal ry = qAbs(roundh)/100. * bound.height();
0210 
0211     const QString fillStyle = saveFill(context);
0212     const QString strokeStyle = saveStroke(context);
0213 
0214     static int roundRectIndex = 0;
0215 
0216     m_svgWriter.startElement("rect");
0217     m_svgWriter.addAttribute("id", QString("roundRect%1").arg(++roundRectIndex));
0218     m_svgWriter.addAttribute("x", bound.x());
0219     m_svgWriter.addAttribute("y", bound.y());
0220     m_svgWriter.addAttribute("width", bound.width());
0221     m_svgWriter.addAttribute("height", bound.height());
0222     m_svgWriter.addAttribute("rx", 0.5*rx);
0223     m_svgWriter.addAttribute("ry", 0.5*ry);
0224     m_svgWriter.addAttribute("style", strokeStyle+fillStyle);
0225     m_svgWriter.endElement(); // rect
0226 }
0227 
0228 void WMFImportParser::drawEllipse(Libwmf::WmfDeviceContext &context, int left, int top, int width, int height)
0229 {
0230     QRectF bound = boundBox(left, top, width, height);
0231 
0232     const QString fillStyle = saveFill(context);
0233     const QString strokeStyle = saveStroke(context);
0234 
0235     static int ellipseIndex = 0;
0236 
0237     m_svgWriter.startElement("ellipse");
0238     m_svgWriter.addAttribute("id", QString("ellipse%1").arg(++ellipseIndex));
0239     m_svgWriter.addAttribute("cx", bound.center().x());
0240     m_svgWriter.addAttribute("cy", bound.center().y());
0241     m_svgWriter.addAttribute("rx", 0.5*bound.width());
0242     m_svgWriter.addAttribute("ry", 0.5*bound.height());
0243     m_svgWriter.addAttribute("style", strokeStyle+fillStyle);
0244     m_svgWriter.endElement(); // ellipse
0245 }
0246 
0247 void WMFImportParser::drawArc(Libwmf::WmfDeviceContext &context, int x, int y, int w, int h, int aStart, int aLen)
0248 {
0249     const qreal a1 = DEG2RAD((aStart * 180) / 2880.0);
0250     const qreal a2 = DEG2RAD((aLen * 180) / 2880.0);
0251     const int largeArc = a2 > M_PI ? 1 : 0;
0252 
0253     QRectF bound = boundBox(x, y, w, h);
0254 
0255     const qreal rx = 0.5*bound.width();
0256     const qreal ry = 0.5*bound.height();
0257     const QPointF p1 = bound.center() + QPointF(rx*cos(a1), -ry*sin(a1));
0258     const QPointF p2 = bound.center() + QPointF(rx*cos(a1+a2), -ry*sin(a1+a2));
0259 
0260     QString path =
0261             QString("M%1,%2 ").arg(p1.x()).arg(p1.y()) +
0262             QString("A%1,%2 0 %5 0 %3,%4").arg(rx).arg(ry).arg(p2.x()).arg(p2.y()).arg(largeArc);
0263 
0264     const QString strokeStyle = saveStroke(context);
0265 
0266     static int arcIndex = 0;
0267 
0268     m_svgWriter.startElement("path");
0269     m_svgWriter.addAttribute("id", QString("arc%1").arg(++arcIndex));
0270     m_svgWriter.addAttribute("d", path);
0271     m_svgWriter.addAttribute("style", strokeStyle+"fill:none");
0272     m_svgWriter.endElement(); // path
0273 }
0274 
0275 void WMFImportParser::drawPie(Libwmf::WmfDeviceContext &context, int x, int y, int w, int h, int aStart, int aLen)
0276 {
0277     const qreal a1 = DEG2RAD((aStart * 180) / 2880.0);
0278     const qreal a2 = DEG2RAD((aLen * 180) / 2880.0);
0279     const int largeArc = a2 > M_PI ? 1 : 0;
0280 
0281     QRectF bound = boundBox(x, y, w, h);
0282 
0283     const qreal rx = 0.5*bound.width();
0284     const qreal ry = 0.5*bound.height();
0285     const QPointF p1 = bound.center() + QPointF(rx*cos(a1), -ry*sin(a1));
0286     const QPointF p2 = bound.center() + QPointF(rx*cos(a1+a2), -ry*sin(a1+a2));
0287 
0288     QString path =
0289             QString("M%1,%2 ").arg(bound.center().x()).arg(bound.center().y()) +
0290             QString("L%1,%2 ").arg(p1.x()).arg(p1.y()) +
0291             QString("A%1,%2 0 %5 0 %3,%4 ").arg(rx).arg(ry).arg(p2.x()).arg(p2.y()).arg(largeArc) +
0292             QString("L%1,%2").arg(bound.center().x()).arg(bound.center().y());
0293 
0294     const QString fillStyle = saveFill(context);
0295     const QString strokeStyle = saveStroke(context);
0296 
0297     static int pieIndex = 0;
0298 
0299     m_svgWriter.startElement("path");
0300     m_svgWriter.addAttribute("id", QString("pie%1").arg(++pieIndex));
0301     m_svgWriter.addAttribute("d", path);
0302     m_svgWriter.addAttribute("style", strokeStyle+fillStyle);
0303     m_svgWriter.endElement(); // path
0304 }
0305 
0306 void WMFImportParser::drawChord(Libwmf::WmfDeviceContext &context, int x, int y, int w, int h, int aStart, int aLen)
0307 {
0308     const qreal a1 = DEG2RAD((aStart * 180) / 2880.0);
0309     const qreal a2 = DEG2RAD((aLen * 180) / 2880.0);
0310     const int largeArc = a2 > M_PI ? 1 : 0;
0311 
0312     QRectF bound = boundBox(x, y, w, h);
0313 
0314     const qreal rx = 0.5*bound.width();
0315     const qreal ry = 0.5*bound.height();
0316     const QPointF p1 = bound.center() + QPointF(rx*cos(a1), -ry*sin(a1));
0317     const QPointF p2 = bound.center() + QPointF(rx*cos(a1+a2), -ry*sin(a1+a2));
0318 
0319     QString path =
0320             QString("M%1,%2 ").arg(p1.x()).arg(p1.y()) +
0321             QString("A%1,%2 0 %5 0 %3,%4 ").arg(rx).arg(ry).arg(p2.x()).arg(p2.y()).arg(largeArc) +
0322             QString("L%1,%2").arg(p1.x()).arg(p1.y());
0323 
0324     const QString fillStyle = saveFill(context);
0325     const QString strokeStyle = saveStroke(context);
0326 
0327     static int chordKIndex = 0;
0328 
0329     m_svgWriter.startElement("path");
0330     m_svgWriter.addAttribute("id", QString("chord%1").arg(++chordKIndex));
0331     m_svgWriter.addAttribute("d", path);
0332     m_svgWriter.addAttribute("style", strokeStyle+fillStyle);
0333     m_svgWriter.endElement(); // path
0334 }
0335 
0336 void WMFImportParser::drawPolyline(Libwmf::WmfDeviceContext &context, const QPolygon &pa)
0337 {
0338     QString points;
0339     const int pointCount = pa.size();
0340     if (pointCount <= 1)
0341         return;
0342 
0343     QPointF p;
0344     // There exists a problem on msvc with for(each) and QVector<QPoint>
0345     for (int i = 0; i < pa.count(); ++i) {
0346         const QPoint &point(pa[i]);
0347         p = coord(point);
0348         points += QString("%1,%2 ").arg(p.x()).arg(p.y());
0349     }
0350 
0351     const QString strokeStyle = saveStroke(context);
0352 
0353     static int polylineIndex = 0;
0354 
0355     m_svgWriter.startElement("polyline");
0356     m_svgWriter.addAttribute("id", QString("polyline%1").arg(++polylineIndex));
0357     m_svgWriter.addAttribute("points", points);
0358     m_svgWriter.addAttribute("style", strokeStyle+"fill:none");
0359     m_svgWriter.endElement(); // polyline
0360 }
0361 
0362 void WMFImportParser::drawPolygon(Libwmf::WmfDeviceContext &context, const QPolygon &pa)
0363 {
0364     QString points;
0365     const int pointCount = pa.size();
0366     if (pointCount <= 1)
0367         return;
0368 
0369     QPointF p;
0370     // There exists a problem on msvc with for(each) and QVector<QPoint>
0371     for (int i = 0; i < pa.count(); ++i) {
0372         const QPoint &point(pa[i]);
0373         p = coord(point);
0374         points += QString("%1,%2 ").arg(p.x()).arg(p.y());
0375     }
0376 
0377     const QString fillStyle = saveFill(context);
0378     const QString strokeStyle = saveStroke(context);
0379 
0380     static int polygonIndex = 0;
0381 
0382     m_svgWriter.startElement("polygon");
0383     m_svgWriter.addAttribute("id", QString("polygon%1").arg(++polygonIndex));
0384     m_svgWriter.addAttribute("points", points);
0385     m_svgWriter.addAttribute("style", strokeStyle+fillStyle);
0386     m_svgWriter.endElement(); // polygon
0387 }
0388 
0389 void WMFImportParser::drawPolyPolygon(Libwmf::WmfDeviceContext &context, QList<QPolygon>& listPa)
0390 {
0391     if (listPa.isEmpty())
0392         return;
0393 
0394     QString path;
0395     QPointF p;
0396     foreach(const QPolygon &poly, listPa) {
0397         int pointCount = poly.size();
0398         if(pointCount <= 1)
0399             continue;
0400         p = coord(poly[0]);
0401         path += QString("M%1,%2 L").arg(p.x()).arg(p.y());
0402         for(int i = 1; i < pointCount; ++i) {
0403             p = coord(poly[i]);
0404             path += QString("%1,%2 ").arg(p.x()).arg(p.y());
0405         }
0406         path.append("Z ");
0407     }
0408 
0409     const QString fillStyle = saveFill(context);
0410     const QString strokeStyle = saveStroke(context);
0411 
0412     static int polyPolygonIndex = 0;
0413 
0414     m_svgWriter.startElement("path");
0415     m_svgWriter.addAttribute("id", QString("polyPolygon%1").arg(++polyPolygonIndex));
0416     m_svgWriter.addAttribute("d", path);
0417     m_svgWriter.addAttribute("style", strokeStyle+fillStyle);
0418     m_svgWriter.endElement(); // path
0419 }
0420 
0421 void WMFImportParser::drawImage(Libwmf::WmfDeviceContext &/*context*/, int x, int y, const QImage &rawImage, int sx, int sy, int sw, int sh)
0422 {
0423     QPoint imgOrg(qMax(sx, 0), qMax(sy, 0));
0424     QSize imgExt = QSize(rawImage.width()-imgOrg.x(), rawImage.height()-imgOrg.y());
0425     if (sw > 0) {
0426         imgExt.rwidth() = qMin(imgExt.width(), sw);
0427     }
0428     if (sh > 0) {
0429         imgExt.rheight() = qMin(imgExt.height(), sh);
0430     }
0431     QImage image = rawImage.copy(QRect(imgOrg, imgExt));
0432 
0433     QByteArray ba;
0434     QBuffer buffer(&ba);
0435     if(!buffer.open(QIODevice::WriteOnly))
0436         return;
0437     if (!image.save(&buffer, "PNG"))
0438         return;
0439 
0440     static int imageIndex = 0;
0441 
0442     const QPointF pos = coord(QPoint(x, y));
0443     const QSizeF s = size(image.size());
0444 
0445     m_svgWriter.startElement("image");
0446     m_svgWriter.addAttribute("id", QString("image%1").arg(++imageIndex));
0447     m_svgWriter.addAttribute("x", pos.x());
0448     m_svgWriter.addAttribute("y", pos.y());
0449     m_svgWriter.addAttribute("width", s.width());
0450     m_svgWriter.addAttribute("height", s.height());
0451     m_svgWriter.addAttribute("xlink:href", "data:image/png;base64," + ba.toBase64());
0452     m_svgWriter.endElement(); // image
0453 }
0454 
0455 void WMFImportParser::patBlt(Libwmf::WmfDeviceContext &/*context*/, int x, int y, int width, int height, quint32 rasterOperation)
0456 {
0457     // Not Yet Implemented
0458     Q_UNUSED(x);
0459     Q_UNUSED(y);
0460     Q_UNUSED(width);
0461     Q_UNUSED(height);
0462     Q_UNUSED(rasterOperation);
0463 }
0464 
0465 void WMFImportParser::drawText(Libwmf::WmfDeviceContext &context, int x, int y, const QString& text)
0466 {
0467     if (context.textAlign & TA_UPDATECP) {
0468         // (left, top) position = current logical position
0469         x = context.currentPosition.x();
0470         y = context.currentPosition.y();
0471     }
0472 
0473     QFontMetrics metrics(context.font);
0474 
0475     if (context.textAlign & TA_BOTTOM) {
0476         y -= metrics.descent();
0477     } else if(context.textAlign & TA_BASELINE) {
0478         // nothing to do here
0479     } else { // TA_TOP
0480         // this the is the default
0481         y += metrics.ascent();
0482     }
0483 
0484     static int textIndex = 0;
0485 
0486     const QPointF pos = coord(QPoint(x, y));
0487 
0488     m_svgWriter.startElement("text");
0489     m_svgWriter.addAttribute("id", QString("text%1").arg(++textIndex));
0490     m_svgWriter.addAttribute("x", pos.x());
0491     m_svgWriter.addAttribute("y", pos.y());
0492     if (context.textAlign & TA_CENTER)
0493         m_svgWriter.addAttribute("text-anchor", "middle");
0494     else if (context.textAlign & TA_RIGHT)
0495         m_svgWriter.addAttribute("text-anchor", "end");
0496     m_svgWriter.addAttribute("font-family", context.font.family());
0497     // adjust font size
0498     m_svgWriter.addAttributePt("font-size", size(QSize(0, context.font.pointSize())).height());
0499     if (context.font.bold())
0500         m_svgWriter.addAttribute("font-weight", "bold");
0501     if (context.font.italic())
0502         m_svgWriter.addAttribute("font-style", "italic");
0503     if (context.font.underline())
0504         m_svgWriter.addAttribute("text-decoration", "underline");
0505     m_svgWriter.addAttribute("stroke", context.foregroundTextColor.name());
0506 
0507     if (context.escapement) {
0508         // we rotate around the anchor point
0509         // rotation is in 1/10th of a degree
0510         QString transform =
0511                 QString("translate(%1,%2) ").arg(pos.x()).arg(pos.y()) +
0512                 QString("rotate(%1) ").arg(qreal(context.escapement) / -10.0) +
0513                 QString("translate(%1,%2)").arg(-pos.x()).arg(-pos.y());
0514         m_svgWriter.addAttribute("transform", transform);
0515     }
0516 
0517     m_svgWriter.addTextNode(text);
0518 
0519     m_svgWriter.endElement(); // text
0520 }
0521 
0522 //-----------------------------------------------------------------------------
0523 // Utilities
0524 
0525 QString WMFImportParser::saveStroke(Libwmf::WmfDeviceContext &context)
0526 {
0527     if(context.pen.style() == Qt::NoPen) {
0528         return "stroke:none;";
0529     }
0530 
0531     const qreal width = context.pen.width() > qreal(1.0) ? qMax(qreal(1.0), context.pen.width() * m_scaleX) : qreal(1.0);
0532 
0533     QString strokeStyle;
0534 
0535     strokeStyle += QString("stroke:%1;").arg(context.pen.color().name());
0536     strokeStyle += QString("stroke-width:%1;").arg(width);
0537     if (context.pen.capStyle() == Qt::FlatCap)
0538         strokeStyle += "stroke-linecap:butt;";
0539     else if (context.pen.capStyle() == Qt::RoundCap)
0540         strokeStyle += "stroke-linecap:round;";
0541     else if (context.pen.capStyle() == Qt::SquareCap)
0542         strokeStyle += "stroke-linecap:square;";
0543 
0544     if (context.pen.joinStyle() == Qt::MiterJoin) {
0545         strokeStyle += "stroke-linejoin:miter;";
0546         strokeStyle += QString("stroke-miterlimit:%1;").arg(context.pen.miterLimit());
0547     } else if (context.pen.joinStyle() == Qt::RoundJoin)
0548         strokeStyle += "stroke-linejoin:round;";
0549     else if (context.pen.joinStyle() == Qt::BevelJoin)
0550         strokeStyle += "stroke-linejoin:bevel;";
0551 
0552     // dash
0553     if (context.pen.style() > Qt::SolidLine) {
0554         qreal dashFactor = width;
0555 
0556         if (context.pen.dashOffset() != 0)
0557             strokeStyle += QString("stroke-dashoffset:%1;").arg(dashFactor * context.pen.dashOffset());
0558 
0559         QString dashStr;
0560         const QVector<qreal> dashes = context.pen.dashPattern();
0561         int dashCount = dashes.size();
0562         for (int i = 0; i < dashCount; ++i) {
0563             if (i > 0)
0564                 dashStr += ",";
0565             dashStr += QString("%1").arg(dashes[i] * dashFactor);
0566         }
0567         strokeStyle += QString("stroke-dasharray:%1;").arg(dashStr);
0568     }
0569 
0570     return strokeStyle;
0571 }
0572 
0573 QString WMFImportParser::saveFill(Libwmf::WmfDeviceContext &context)
0574 {
0575     if (context.brush.style() == Qt::NoBrush) {
0576         return "fill:none;";
0577     }
0578 
0579     QString fillStyle;
0580 
0581     if (context.brush.style() == Qt::SolidPattern) {
0582         fillStyle = QString("fill:%1;").arg(context.brush.color().name());
0583         if (context.brush.color().alphaF() < 1.0)
0584             fillStyle += QString("fill-opacity:%1;").arg(context.brush.color().alphaF());
0585 
0586         return fillStyle;
0587     }
0588 
0589     static int fillIndex = 0;
0590     QString fillId = QString("fill%1").arg(++fillIndex);
0591 
0592     switch (context.brush.style()) {
0593     case Qt::TexturePattern: {
0594         QImage texture = context.brush.textureImage();
0595         m_svgWriter.startElement("pattern");
0596         m_svgWriter.addAttribute("id", fillId);
0597         m_svgWriter.addAttribute("x", 0);
0598         m_svgWriter.addAttribute("y", 0);
0599         m_svgWriter.addAttribute("width", texture.size().width());
0600         m_svgWriter.addAttribute("height", texture.size().height());
0601         m_svgWriter.addAttribute("patternUnits", "userSpaceOnUse");
0602 
0603         m_svgWriter.addAttribute("viewBox", QString("0 0 %1 %2").arg(texture.size().width()).arg(texture.size().height()));
0604 
0605         m_svgWriter.startElement("image");
0606         m_svgWriter.addAttribute("x", "0");
0607         m_svgWriter.addAttribute("y", "0");
0608         m_svgWriter.addAttribute("width", QString("%1px").arg(texture.size().width()));
0609         m_svgWriter.addAttribute("height", QString("%1px").arg(texture.size().height()));
0610 
0611         QByteArray ba;
0612         QBuffer buffer(&ba);
0613         buffer.open(QIODevice::WriteOnly);
0614         if (texture.save(&buffer, "PNG")) {
0615             m_svgWriter.addAttribute("xlink:href", "data:image/png;base64," + ba.toBase64());
0616         }
0617 
0618         m_svgWriter.endElement(); // image
0619         m_svgWriter.endElement(); // pattern
0620         break;
0621     }
0622     case Qt::HorPattern:
0623         m_svgWriter.startElement("pattern");
0624         m_svgWriter.addAttribute("id", fillId);
0625         m_svgWriter.addAttribute("x", 0);
0626         m_svgWriter.addAttribute("y", 0);
0627         m_svgWriter.addAttribute("width", 30);
0628         m_svgWriter.addAttribute("height", 30);
0629         m_svgWriter.addAttribute("patternUnits", "userSpaceOnUse");
0630         m_svgWriter.startElement("path");
0631         m_svgWriter.addAttribute("d", "M0,5 L30,5 M0,15 L30,15 M0,25 L30,25");
0632         m_svgWriter.addAttribute("style", "stroke:black;stroke-width:1");
0633         m_svgWriter.endElement(); // path
0634         m_svgWriter.endElement(); // pattern
0635         break;
0636     case Qt::VerPattern:
0637         m_svgWriter.startElement("pattern");
0638         m_svgWriter.addAttribute("id", fillId);
0639         m_svgWriter.addAttribute("x", 0);
0640         m_svgWriter.addAttribute("y", 0);
0641         m_svgWriter.addAttribute("width", 30);
0642         m_svgWriter.addAttribute("height", 30);
0643         m_svgWriter.addAttribute("patternUnits", "userSpaceOnUse");
0644         m_svgWriter.startElement("path");
0645         m_svgWriter.addAttribute("d", "M5,0 L5,30 M15,0 L15,30 M25,0 L25,30");
0646         m_svgWriter.addAttribute("style", "stroke:black;stroke-width:1");
0647         m_svgWriter.endElement(); // path
0648         m_svgWriter.endElement(); // pattern
0649         break;
0650     case Qt::CrossPattern:
0651         m_svgWriter.startElement("pattern");
0652         m_svgWriter.addAttribute("id", fillId);
0653         m_svgWriter.addAttribute("x", 0);
0654         m_svgWriter.addAttribute("y", 0);
0655         m_svgWriter.addAttribute("width", 30);
0656         m_svgWriter.addAttribute("height", 30);
0657         m_svgWriter.addAttribute("patternUnits", "userSpaceOnUse");
0658         m_svgWriter.startElement("path");
0659         m_svgWriter.addAttribute("d", "M5,0 L5,30 M15,0 L15,30 M25,0 L25,30 M0,5 L30,5 M0,15 L30,15 M0,25 L30,25");
0660         m_svgWriter.addAttribute("style", "stroke:black;stroke-width:1");
0661         m_svgWriter.endElement(); // path
0662         m_svgWriter.endElement(); // pattern
0663         break;
0664     case Qt::BDiagPattern:
0665         m_svgWriter.startElement("pattern");
0666         m_svgWriter.addAttribute("id", fillId);
0667         m_svgWriter.addAttribute("x", 0);
0668         m_svgWriter.addAttribute("y", 0);
0669         m_svgWriter.addAttribute("width", 30);
0670         m_svgWriter.addAttribute("height", 30);
0671         m_svgWriter.addAttribute("patternUnits", "userSpaceOnUse");
0672         m_svgWriter.startElement("path");
0673         m_svgWriter.addAttribute("d", "M0,30 L30,0 M0,15 L15,0 M15,30 L30,15");
0674         m_svgWriter.addAttribute("style", "stroke:black;stroke-width:1");
0675         m_svgWriter.endElement(); // path
0676         m_svgWriter.endElement(); // pattern
0677         break;
0678     case Qt::FDiagPattern:
0679         m_svgWriter.startElement("pattern");
0680         m_svgWriter.addAttribute("id", fillId);
0681         m_svgWriter.addAttribute("x", 0);
0682         m_svgWriter.addAttribute("y", 0);
0683         m_svgWriter.addAttribute("width", 30);
0684         m_svgWriter.addAttribute("height", 30);
0685         m_svgWriter.addAttribute("patternUnits", "userSpaceOnUse");
0686         m_svgWriter.startElement("path");
0687         m_svgWriter.addAttribute("d", "M0,00 L30,30 M0,15 L15,30 M15,0 L30,15");
0688         m_svgWriter.addAttribute("style", "stroke:black;stroke-width:1");
0689         m_svgWriter.endElement(); // path
0690         m_svgWriter.endElement(); // pattern
0691         break;
0692     case Qt::DiagCrossPattern:
0693         m_svgWriter.startElement("pattern");
0694         m_svgWriter.addAttribute("id", fillId);
0695         m_svgWriter.addAttribute("x", 0);
0696         m_svgWriter.addAttribute("y", 0);
0697         m_svgWriter.addAttribute("width", 30);
0698         m_svgWriter.addAttribute("height", 30);
0699         m_svgWriter.addAttribute("patternUnits", "userSpaceOnUse");
0700         m_svgWriter.startElement("path");
0701         m_svgWriter.addAttribute("d", "M0,30 L30,0 M0,15 L15,0 M15,30 L30,15 M0,00 L30,30 M0,15 L15,30 M15,0 L30,15");
0702         m_svgWriter.addAttribute("style", "stroke:black;stroke-width:1");
0703         m_svgWriter.endElement(); // path
0704         m_svgWriter.endElement(); // pattern
0705         break;
0706     default:
0707         debugWmf << "unsupported brush style:" << context.brush.style();
0708         return QString();
0709     }
0710 
0711     fillStyle = QString("fill:url(#%1);").arg(fillId);
0712 
0713     if (context.polyFillMode == Libwmf::ALTERNATE)
0714         fillStyle += "fill-rule:evenodd;";
0715 
0716     return fillStyle;
0717 }
0718 
0719 void  WMFImportParser::setCompositionMode(QPainter::CompositionMode)
0720 {
0721     //TODO
0722 }
0723 
0724 QRectF WMFImportParser::boundBox(int left, int top, int width, int height)
0725 {
0726     const int l = qMin(left, left+width);
0727     const int t = qMin(top, top+height);
0728     const int w = qAbs(width);
0729     const int h = qAbs(height);
0730 
0731     return QRectF(coord(QPoint(l,t)), size(QSize(w, h)));
0732 }
0733 
0734 QPointF WMFImportParser::coord(const QPoint &p)
0735 {
0736     const qreal dx = m_viewport.org.x()-m_window.org.x();
0737     const qreal dy = m_viewport.org.y()-m_window.org.y();
0738     const qreal x = (p.x() + dx) * m_scaleX;
0739     const qreal y = (p.y() + dy) * m_scaleY;
0740     return QPointF(x, y);
0741 }
0742 
0743 QSizeF WMFImportParser::size(const QSize &s)
0744 {
0745     return QSizeF(m_scaleX *s.width(), m_scaleY*s.height());
0746 }
0747 
0748 void WMFImportParser::updateTransform()
0749 {
0750     if (m_window.extIsValid && m_viewport.extIsValid) {
0751         m_scaleX = m_viewport.ext.width() / m_window.ext.width();
0752         m_scaleY = m_viewport.ext.height() / m_window.ext.height();
0753     }
0754     debugWmf << "window:" << QRectF(m_window.org, m_window.ext);
0755     debugWmf << "viewport:" << QRectF(m_viewport.org, m_viewport.ext);
0756     debugWmf << "scale:" << m_scaleX << m_scaleY;
0757 }