Warning, file /office/calligra/libs/odf/KoOdfGraphicStyles.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    Copyright (C) 2004-2006 David Faure <faure@kde.org>
0003    Copyright (C) 2007 Jan Hambrecht <jaham@gmx.net>
0004    Copyright (C) 2007-2008,2010-2011 Thorsten Zachmann <zachmann@kde.org>
0005    Copyright (C) 2011 Lukáš Tvrdý <lukas.tvrdy@ixonos.com>
0006 
0007    This library is free software; you can redistribute it and/or
0008    modify it under the terms of the GNU Library General Public
0009    License as published by the Free Software Foundation; either
0010    version 2 of the License, or (at your option) any later version.
0011 
0012    This library is distributed in the hope that it will be useful,
0013    but WITHOUT ANY WARRANTY; without even the implied warranty of
0014    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0015    Library General Public License for more details.
0016 
0017    You should have received a copy of the GNU Library General Public License
0018    along with this library; see the file COPYING.LIB.  If not, write to
0019    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0020  * Boston, MA 02110-1301, USA.
0021 */
0022 
0023 #include "KoOdfGraphicStyles.h"
0024 
0025 #include <QBrush>
0026 #include <QBuffer>
0027 #include <QPen>
0028 
0029 #include <OdfDebug.h>
0030 
0031 #include <KoGenStyles.h>
0032 #include <KoStyleStack.h>
0033 #include <KoUnit.h>
0034 #include <KoXmlNS.h>
0035 #include <KoXmlWriter.h>
0036 
0037 #include "KoOdfStylesReader.h"
0038 
0039 void KoOdfGraphicStyles::saveOdfFillStyle(KoGenStyle &styleFill, KoGenStyles& mainStyles, const QBrush & brush)
0040 {
0041     KoGenStyle::Type type = styleFill.type();
0042     KoGenStyle::PropertyType propertyType = (type == KoGenStyle::GraphicStyle || type == KoGenStyle::GraphicAutoStyle ||
0043                                              type == KoGenStyle::DrawingPageStyle || type == KoGenStyle::DrawingPageAutoStyle )
0044                                             ? KoGenStyle::DefaultType : KoGenStyle::GraphicType;
0045     switch (brush.style()) {
0046     case Qt::Dense1Pattern:
0047         styleFill.addProperty("draw:opacity", "6%", propertyType);
0048         styleFill.addProperty("draw:fill", "solid", propertyType);
0049         styleFill.addProperty("draw:fill-color", brush.color().name(), propertyType);
0050         break;
0051     case Qt::Dense2Pattern:
0052         styleFill.addProperty("draw:opacity", "12%", propertyType);
0053         styleFill.addProperty("draw:fill", "solid", propertyType);
0054         styleFill.addProperty("draw:fill-color", brush.color().name(), propertyType);
0055         break;
0056     case Qt::Dense3Pattern:
0057         styleFill.addProperty("draw:opacity", "37%", propertyType);
0058         styleFill.addProperty("draw:fill", "solid", propertyType);
0059         styleFill.addProperty("draw:fill-color", brush.color().name(), propertyType);
0060         break;
0061     case Qt::Dense4Pattern:
0062         styleFill.addProperty("draw:opacity", "50%", propertyType);
0063         styleFill.addProperty("draw:fill", "solid", propertyType);
0064         styleFill.addProperty("draw:fill-color", brush.color().name(), propertyType);
0065         break;
0066     case Qt::Dense5Pattern:
0067         styleFill.addProperty("draw:opacity", "63%", propertyType);
0068         styleFill.addProperty("draw:fill", "solid", propertyType);
0069         styleFill.addProperty("draw:fill-color", brush.color().name(), propertyType);
0070         break;
0071     case Qt::Dense6Pattern:
0072         styleFill.addProperty("draw:opacity", "88%", propertyType);
0073         styleFill.addProperty("draw:fill", "solid", propertyType);
0074         styleFill.addProperty("draw:fill-color", brush.color().name(), propertyType);
0075         break;
0076     case Qt::Dense7Pattern:
0077         styleFill.addProperty("draw:opacity", "94%", propertyType);
0078         styleFill.addProperty("draw:fill", "solid", propertyType);
0079         styleFill.addProperty("draw:fill-color", brush.color().name(), propertyType);
0080         break;
0081     case Qt::LinearGradientPattern:
0082     case Qt::RadialGradientPattern:
0083     case Qt::ConicalGradientPattern:
0084         styleFill.addProperty("draw:fill", "gradient", propertyType);
0085         styleFill.addProperty("draw:fill-gradient-name", saveOdfGradientStyle(mainStyles, brush), propertyType);
0086         break;
0087     case Qt::HorPattern:
0088     case Qt::VerPattern:
0089     case Qt::CrossPattern:
0090     case Qt::BDiagPattern:
0091     case Qt::FDiagPattern:
0092     case Qt::DiagCrossPattern:
0093         styleFill.addProperty("draw:fill", "hatch", propertyType);
0094         styleFill.addProperty("draw:fill-hatch-name", saveOdfHatchStyle(mainStyles, brush), propertyType);
0095         break;
0096     case Qt::SolidPattern:
0097         styleFill.addProperty("draw:fill", "solid", propertyType);
0098         styleFill.addProperty("draw:fill-color", brush.color().name(), propertyType);
0099         if (! brush.isOpaque())
0100             styleFill.addProperty("draw:opacity", QString("%1%").arg(brush.color().alphaF() * 100.0), propertyType);
0101         break;
0102     case Qt::NoBrush:
0103     default:
0104         styleFill.addProperty("draw:fill", "none", propertyType);
0105         break;
0106     }
0107 }
0108 
0109 void KoOdfGraphicStyles::saveOdfStrokeStyle(KoGenStyle &styleStroke, KoGenStyles &mainStyles, const QPen &pen)
0110 {
0111     // TODO implement all possibilities
0112     switch (pen.style()) {
0113     case Qt::NoPen:
0114         styleStroke.addProperty("draw:stroke", "none", KoGenStyle::GraphicType);
0115         return;
0116     case Qt::SolidLine:
0117         styleStroke.addProperty("draw:stroke", "solid", KoGenStyle::GraphicType);
0118         break;
0119     default: { // must be a dashed line
0120         styleStroke.addProperty("draw:stroke", "dash", KoGenStyle::GraphicType);
0121         // save stroke dash (14.14.7) which is severely limited, but still
0122         KoGenStyle dashStyle(KoGenStyle::StrokeDashStyle);
0123         dashStyle.addAttribute("draw:style", "rect");
0124         QVector<qreal> dashes = pen.dashPattern();
0125         dashStyle.addAttribute("draw:dots1", static_cast<int>(1));
0126         dashStyle.addAttributePt("draw:dots1-length", dashes[0]*pen.widthF());
0127         dashStyle.addAttributePt("draw:distance", dashes[1]*pen.widthF());
0128         if (dashes.size() > 2) {
0129             dashStyle.addAttribute("draw:dots2", static_cast<int>(1));
0130             dashStyle.addAttributePt("draw:dots2-length", dashes[2]*pen.widthF());
0131         }
0132         QString dashStyleName = mainStyles.insert(dashStyle, "dash");
0133         styleStroke.addProperty("draw:stroke-dash", dashStyleName, KoGenStyle::GraphicType);
0134         break;
0135     }
0136     }
0137 
0138     if (pen.brush().gradient()) {
0139         styleStroke.addProperty("calligra:stroke-gradient", saveOdfGradientStyle(mainStyles, pen.brush()), KoGenStyle::GraphicType);
0140     }
0141     else {
0142         styleStroke.addProperty("svg:stroke-color", pen.color().name(), KoGenStyle::GraphicType);
0143         styleStroke.addProperty("svg:stroke-opacity", QString("%1").arg(pen.color().alphaF()), KoGenStyle::GraphicType);
0144     }
0145     styleStroke.addPropertyPt("svg:stroke-width", pen.widthF(), KoGenStyle::GraphicType);
0146 
0147     switch (pen.joinStyle()) {
0148     case Qt::MiterJoin:
0149         styleStroke.addProperty("draw:stroke-linejoin", "miter", KoGenStyle::GraphicType);
0150         break;
0151     case Qt::BevelJoin:
0152         styleStroke.addProperty("draw:stroke-linejoin", "bevel", KoGenStyle::GraphicType);
0153         break;
0154     case Qt::RoundJoin:
0155         styleStroke.addProperty("draw:stroke-linejoin", "round", KoGenStyle::GraphicType);
0156         break;
0157     default:
0158         styleStroke.addProperty("draw:stroke-linejoin", "miter", KoGenStyle::GraphicType);
0159         styleStroke.addProperty("calligra:stroke-miterlimit", QString("%1").arg(pen.miterLimit()), KoGenStyle::GraphicType);
0160         break;
0161     }
0162     switch (pen.capStyle()) {
0163     case Qt::RoundCap:
0164         styleStroke.addProperty("svg:stroke-linecap", "round", KoGenStyle::GraphicType);
0165         break;
0166     case Qt::SquareCap:
0167         styleStroke.addProperty("svg:stroke-linecap", "square", KoGenStyle::GraphicType);
0168         break;
0169     default:
0170         styleStroke.addProperty("svg:stroke-linecap", "butt", KoGenStyle::GraphicType);
0171         break;
0172     }
0173 }
0174 
0175 QString KoOdfGraphicStyles::saveOdfHatchStyle(KoGenStyles& mainStyles, const QBrush &brush)
0176 {
0177     KoGenStyle hatchStyle(KoGenStyle::HatchStyle /*no family name*/);
0178     hatchStyle.addAttribute("draw:color", brush.color().name());
0179     //hatchStyle.addAttribute( "draw:distance", m_distance ); not implemented into Stage
0180     switch (brush.style()) {
0181     case Qt::HorPattern:
0182         hatchStyle.addAttribute("draw:style", "single");
0183         hatchStyle.addAttribute("draw:rotation", 0);
0184         break;
0185     case Qt::BDiagPattern:
0186         hatchStyle.addAttribute("draw:style", "single");
0187         hatchStyle.addAttribute("draw:rotation", 450);
0188         break;
0189     case Qt::VerPattern:
0190         hatchStyle.addAttribute("draw:style", "single");
0191         hatchStyle.addAttribute("draw:rotation", 900);
0192         break;
0193     case Qt::FDiagPattern:
0194         hatchStyle.addAttribute("draw:style", "single");
0195         hatchStyle.addAttribute("draw:rotation", 1350);
0196         break;
0197     case Qt::CrossPattern:
0198         hatchStyle.addAttribute("draw:style", "double");
0199         hatchStyle.addAttribute("draw:rotation", 0);
0200         break;
0201     case Qt::DiagCrossPattern:
0202         hatchStyle.addAttribute("draw:style", "double");
0203         hatchStyle.addAttribute("draw:rotation", 450);
0204         break;
0205     default:
0206         break;
0207     }
0208 
0209     return mainStyles.insert(hatchStyle, "hatch");
0210 }
0211 
0212 QString KoOdfGraphicStyles::saveOdfGradientStyle(KoGenStyles &mainStyles, const QBrush &brush)
0213 {
0214     KoGenStyle gradientStyle;
0215     if (brush.style() == Qt::RadialGradientPattern) {
0216         const QRadialGradient *gradient = static_cast<const QRadialGradient*>(brush.gradient());
0217         gradientStyle = KoGenStyle(KoGenStyle::RadialGradientStyle /*no family name*/);
0218         gradientStyle.addAttributePercent("svg:cx", gradient->center().x() * 100);
0219         gradientStyle.addAttributePercent("svg:cy", gradient->center().y() * 100);
0220         gradientStyle.addAttributePercent("svg:r",  gradient->radius() * 100);
0221         gradientStyle.addAttributePercent("svg:fx", gradient->focalPoint().x() * 100);
0222         gradientStyle.addAttributePercent("svg:fy", gradient->focalPoint().y() * 100);
0223     } else if (brush.style() == Qt::LinearGradientPattern) {
0224         const QLinearGradient *gradient = static_cast<const QLinearGradient*>(brush.gradient());
0225         gradientStyle = KoGenStyle(KoGenStyle::LinearGradientStyle /*no family name*/);
0226         gradientStyle.addAttributePercent("svg:x1", gradient->start().x() * 100);
0227         gradientStyle.addAttributePercent("svg:y1", gradient->start().y() * 100);
0228         gradientStyle.addAttributePercent("svg:x2", gradient->finalStop().x() * 100);
0229         gradientStyle.addAttributePercent("svg:y2", gradient->finalStop().y() * 100);
0230     } else if (brush.style() == Qt::ConicalGradientPattern) {
0231         const QConicalGradient * gradient = static_cast<const QConicalGradient*>(brush.gradient());
0232         gradientStyle = KoGenStyle(KoGenStyle::ConicalGradientStyle /*no family name*/);
0233         gradientStyle.addAttributePercent("svg:cx", gradient->center().x() * 100);
0234         gradientStyle.addAttributePercent("svg:cy", gradient->center().y() * 100);
0235         gradientStyle.addAttribute("draw:angle", QString("%1").arg(gradient->angle()));
0236     }
0237     const QGradient * gradient = brush.gradient();
0238     if (gradient->spread() == QGradient::RepeatSpread)
0239         gradientStyle.addAttribute("svg:spreadMethod", "repeat");
0240     else if (gradient->spread() == QGradient::ReflectSpread)
0241         gradientStyle.addAttribute("svg:spreadMethod", "reflect");
0242     else
0243         gradientStyle.addAttribute("svg:spreadMethod", "pad");
0244 
0245     if (! brush.transform().isIdentity()) {
0246         gradientStyle.addAttribute("svg:gradientTransform", saveTransformation(brush.transform()));
0247     }
0248 
0249     QBuffer buffer;
0250     buffer.open(QIODevice::WriteOnly);
0251     KoXmlWriter elementWriter(&buffer);    // TODO pass indentation level
0252 
0253     // save stops
0254     QGradientStops stops = gradient->stops();
0255     foreach(const QGradientStop & stop, stops) {
0256         elementWriter.startElement("svg:stop");
0257         elementWriter.addAttribute("svg:offset", QString("%1").arg(stop.first));
0258         elementWriter.addAttribute("svg:stop-color", stop.second.name());
0259         if (stop.second.alphaF() < 1.0)
0260             elementWriter.addAttribute("svg:stop-opacity", QString("%1").arg(stop.second.alphaF()));
0261         elementWriter.endElement();
0262     }
0263 
0264     QString elementContents = QString::fromUtf8(buffer.buffer(), buffer.buffer().size());
0265     gradientStyle.addChildElement("svg:stop", elementContents);
0266 
0267     return mainStyles.insert(gradientStyle, "gradient");
0268 }
0269 
0270 QBrush KoOdfGraphicStyles::loadOdfGradientStyle(const KoStyleStack &styleStack, const KoOdfStylesReader & stylesReader, const QSizeF &size)
0271 {
0272     QString styleName = styleStack.property(KoXmlNS::draw, "fill-gradient-name");
0273     return loadOdfGradientStyleByName(stylesReader, styleName, size);
0274 }
0275 
0276 qreal percent(const KoXmlElement &element, const QString &ns, const QString &type, const QString &defaultValue, qreal absolute)
0277 {
0278     qreal tmp = 0.0;
0279     QString value = element.attributeNS(ns, type, defaultValue);
0280     if (value.indexOf('%') > -1) { // percent value
0281         tmp = value.remove('%').toDouble() / 100.0;
0282     }
0283     else { // fixed value
0284         tmp = KoUnit::parseValue(value) / absolute;
0285         // The following is done so that we get the same data as when we save/load.
0286         // This is needed that we get the same values due to rounding differences
0287         // of absolute and relative values.
0288         QString value = QString("%1").arg(tmp * 100.0);
0289         tmp = value.toDouble() / 100;
0290     }
0291 
0292     return tmp;
0293 }
0294 
0295 QBrush KoOdfGraphicStyles::loadOdfGradientStyleByName(const KoOdfStylesReader &stylesReader, const QString &styleName, const QSizeF &size)
0296 {
0297     KoXmlElement* e = stylesReader.drawStyles("gradient").value(styleName);
0298     if (! e)
0299         return QBrush();
0300 
0301     QGradient * gradient = 0;
0302     QTransform transform;
0303 
0304     if (e->namespaceURI() == KoXmlNS::draw && e->localName() == "gradient") {
0305         // FIXME seems like oo renders the gradient start stop color at the center of the
0306         // radial gradient, and the start color at the radius of the radial gradient
0307         // whereas it is not mentioned in the spec how it should be rendered
0308         // note that svg defines that exactly as the opposite as oo does
0309         // so what should we do?
0310         QString type = e->attributeNS(KoXmlNS::draw, "style", QString());
0311         if (type == "radial") {
0312             // Zagge: at the moment the only objectBoundingBox is supported:
0313             // 18.539 svg:gradientUnits
0314             // See §13.2.2 and §13.2.3 of [SVG].
0315             // The default value for this attribute is objectBoundingBox.
0316             // The only value of the svg:gradientUnits attribute is objectBoundingBox.
0317 
0318             qreal cx = KoUnit::parseValue(e->attributeNS(KoXmlNS::draw, "cx", QString()).remove('%'));
0319             qreal cy = KoUnit::parseValue(e->attributeNS(KoXmlNS::draw, "cy", QString()).remove('%'));
0320             gradient = new QRadialGradient(QPointF(cx * 0.01, cy * 0.01), sqrt(0.5));
0321             gradient->setCoordinateMode(QGradient::ObjectBoundingMode);
0322         } else if (type == "linear" || type == "axial") {
0323             QLinearGradient * lg = new QLinearGradient();
0324             lg->setCoordinateMode(QGradient::ObjectBoundingMode);
0325             // Dividing by 10 here because OOo saves as degree * 10
0326             qreal angle = 90 + e->attributeNS(KoXmlNS::draw, "angle", "0").toDouble() / 10;
0327             qreal radius = sqrt(0.5);
0328 
0329             qreal sx = cos(angle * M_PI / 180) * radius;
0330             qreal sy = sin(angle * M_PI / 180) * radius;
0331             lg->setStart(QPointF(0.5 + sx, 0.5 - sy));
0332             lg->setFinalStop(QPointF(0.5 - sx, 0.5 + sy));
0333             gradient = lg;
0334         } else
0335             return QBrush();
0336 
0337         qreal border = 0.01 * e->attributeNS(KoXmlNS::draw, "border", "0").remove('%').toDouble();
0338         QGradientStops stops;
0339         if (type != "axial") {
0340             // In case of radial gradients the colors are reversed, because OOo saves them as the opposite of the SVG direction
0341             // see bug 137639
0342             QGradientStop start;
0343             start.first = (type != "radial") ? border : 1.0 - border;
0344             start.second = QColor(e->attributeNS(KoXmlNS::draw, "start-color", QString()));
0345             start.second.setAlphaF(0.01 * e->attributeNS(KoXmlNS::draw, "start-intensity", "100").remove('%').toDouble());
0346 
0347             QGradientStop end;
0348             end.first = (type != "radial") ? 1.0 : 0.0;
0349             end.second = QColor(e->attributeNS(KoXmlNS::draw, "end-color", QString()));
0350             end.second.setAlphaF(0.01 * e->attributeNS(KoXmlNS::draw, "end-intensity", "100").remove('%').toDouble());
0351 
0352             stops << start << end;
0353         } else {
0354             QGradientStop start;
0355             start.first = 0.5 * border;
0356             start.second = QColor(e->attributeNS(KoXmlNS::draw, "end-color", QString()));
0357             start.second.setAlphaF(0.01 * e->attributeNS(KoXmlNS::draw, "end-intensity", "100").remove('%').toDouble());
0358 
0359             QGradientStop middle;
0360             middle.first = 0.5;
0361             middle.second = QColor(e->attributeNS(KoXmlNS::draw, "start-color", QString()));
0362             middle.second.setAlphaF(0.01 * e->attributeNS(KoXmlNS::draw, "start-intensity", "100").remove('%').toDouble());
0363 
0364             QGradientStop end;
0365             end.first = 1.0 - 0.5 * border;
0366             end.second = QColor(e->attributeNS(KoXmlNS::draw, "end-color", QString()));
0367             end.second.setAlphaF(0.01 * e->attributeNS(KoXmlNS::draw, "end-intensity", "100").remove('%').toDouble());
0368 
0369             stops << start << middle << end;
0370         }
0371 
0372         gradient->setStops(stops);
0373     } else if (e->namespaceURI() == KoXmlNS::svg) {
0374         if (e->localName() == "linearGradient") {
0375             QPointF start, stop;
0376             start.setX(percent(*e, KoXmlNS::svg, "x1", "0%", size.width()));
0377             start.setY(percent(*e, KoXmlNS::svg, "y1", "0%", size.height()));
0378             stop.setX(percent(*e, KoXmlNS::svg, "x2", "100%", size.width()));
0379             stop.setY(percent(*e, KoXmlNS::svg, "y2", "100%", size.height()));
0380             gradient = new QLinearGradient(start, stop);
0381         } else if (e->localName() == "radialGradient") {
0382             QPointF center, focalPoint;
0383             center.setX(percent(*e, KoXmlNS::svg, "cx", "50%", size.width()));
0384             center.setY(percent(*e, KoXmlNS::svg, "cy", "50%", size.height()));
0385             qreal r = percent(*e, KoXmlNS::svg, "r", "50%", sqrt(size.width() * size.width() + size.height() * size.height()));
0386             focalPoint.setX(percent(*e, KoXmlNS::svg, "fx", QString(), size.width()));
0387             focalPoint.setY(percent(*e, KoXmlNS::svg, "fy", QString(), size.height()));
0388             gradient = new QRadialGradient(center, r, focalPoint );
0389         }
0390         if (! gradient)
0391             return QBrush();
0392 
0393         gradient->setCoordinateMode(QGradient::ObjectBoundingMode);
0394 
0395         QString strSpread(e->attributeNS(KoXmlNS::svg, "spreadMethod", "pad"));
0396         if (strSpread == "repeat")
0397             gradient->setSpread(QGradient::RepeatSpread);
0398         else if (strSpread == "reflect")
0399             gradient->setSpread(QGradient::ReflectSpread);
0400         else
0401             gradient->setSpread(QGradient::PadSpread);
0402 
0403         if (e->hasAttributeNS(KoXmlNS::svg, "gradientTransform"))
0404             transform = loadTransformation(e->attributeNS(KoXmlNS::svg, "gradientTransform", QString()));
0405 
0406         QGradientStops stops;
0407 
0408         // load stops
0409         KoXmlElement colorstop;
0410         forEachElement(colorstop, (*e)) {
0411             if (colorstop.namespaceURI() == KoXmlNS::svg && colorstop.localName() == "stop") {
0412                 QGradientStop stop;
0413                 stop.second = QColor(colorstop.attributeNS(KoXmlNS::svg, "stop-color", QString()));
0414                 stop.second.setAlphaF(colorstop.attributeNS(KoXmlNS::svg, "stop-opacity", "1.0").toDouble());
0415                 stop.first = colorstop.attributeNS(KoXmlNS::svg, "offset", "0.0").toDouble();
0416                 stops.append(stop);
0417             }
0418         }
0419         gradient->setStops(stops);
0420     } else if (e->namespaceURI() == KoXmlNS::calligra) {
0421         if (e->localName() == "conicalGradient") {
0422             QPointF center;
0423             center.setX(percent(*e, KoXmlNS::svg, "cx", "50%", size.width()));
0424             center.setY(percent(*e, KoXmlNS::svg, "cy", "50%", size.height()));
0425             qreal angle = KoUnit::parseValue(e->attributeNS(KoXmlNS::draw, "angle", QString()));
0426             gradient = new QConicalGradient(center, angle);
0427             gradient->setCoordinateMode(QGradient::ObjectBoundingMode);
0428 
0429             QString strSpread(e->attributeNS(KoXmlNS::svg, "spreadMethod", "pad"));
0430             if (strSpread == "repeat")
0431                 gradient->setSpread(QGradient::RepeatSpread);
0432             else if (strSpread == "reflect")
0433                 gradient->setSpread(QGradient::ReflectSpread);
0434             else
0435                 gradient->setSpread(QGradient::PadSpread);
0436 
0437             if (e->hasAttributeNS(KoXmlNS::svg, "gradientTransform"))
0438                 transform = loadTransformation(e->attributeNS(KoXmlNS::svg, "gradientTransform", QString()));
0439 
0440             QGradientStops stops;
0441 
0442             // load stops
0443             KoXmlElement colorstop;
0444             forEachElement(colorstop, (*e)) {
0445                 if (colorstop.namespaceURI() == KoXmlNS::svg && colorstop.localName() == "stop") {
0446                     QGradientStop stop;
0447                     stop.second = QColor(colorstop.attributeNS(KoXmlNS::svg, "stop-color", QString()));
0448                     stop.second.setAlphaF(colorstop.attributeNS(KoXmlNS::svg, "stop-opacity", "1.0").toDouble());
0449                     stop.first = colorstop.attributeNS(KoXmlNS::svg, "offset", "0.0").toDouble();
0450                     stops.append(stop);
0451                 }
0452             }
0453             gradient->setStops(stops);
0454         }
0455     }
0456 
0457     if (! gradient)
0458         return QBrush();
0459 
0460     QBrush resultBrush(*gradient);
0461     resultBrush.setTransform(transform);
0462 
0463     delete gradient;
0464     return resultBrush;
0465 }
0466 
0467 QBrush KoOdfGraphicStyles::loadOdfFillStyle(const KoStyleStack &styleStack, const QString & fill,  const KoOdfStylesReader & stylesReader)
0468 {
0469     QBrush tmpBrush; // default brush for "none" is a Qt::NoBrush
0470 
0471     if (fill == "solid") {
0472         tmpBrush.setStyle(Qt::SolidPattern);
0473         if (styleStack.hasProperty(KoXmlNS::draw, "fill-color"))
0474             tmpBrush.setColor(styleStack.property(KoXmlNS::draw, "fill-color"));
0475         if (styleStack.hasProperty(KoXmlNS::draw, "opacity")) {
0476             QString opacity = styleStack.property(KoXmlNS::draw, "opacity");
0477             if (! opacity.isEmpty() && opacity.right(1) == "%") {
0478                 float percent = opacity.leftRef(opacity.length() - 1).toFloat();
0479                 QColor color = tmpBrush.color();
0480                 color.setAlphaF(percent / 100.0);
0481                 tmpBrush.setColor(color);
0482             }
0483         }
0484         //TODO
0485         if (styleStack.hasProperty(KoXmlNS::draw, "transparency")) {
0486             QString transparency = styleStack.property(KoXmlNS::draw, "transparency");
0487             if (transparency == "94%") {
0488                 tmpBrush.setStyle(Qt::Dense1Pattern);
0489             } else if (transparency == "88%") {
0490                 tmpBrush.setStyle(Qt::Dense2Pattern);
0491             } else if (transparency == "63%") {
0492                 tmpBrush.setStyle(Qt::Dense3Pattern);
0493 
0494             } else if (transparency == "50%") {
0495                 tmpBrush.setStyle(Qt::Dense4Pattern);
0496 
0497             } else if (transparency == "37%") {
0498                 tmpBrush.setStyle(Qt::Dense5Pattern);
0499 
0500             } else if (transparency == "12%") {
0501                 tmpBrush.setStyle(Qt::Dense6Pattern);
0502 
0503             } else if (transparency == "6%") {
0504                 tmpBrush.setStyle(Qt::Dense7Pattern);
0505 
0506             } else
0507                 debugOdf << " transparency is not defined into Stage :" << transparency;
0508         }
0509     } else if (fill == "hatch") {
0510         QString style = styleStack.property(KoXmlNS::draw, "fill-hatch-name");
0511         debugOdf << " hatch style is  :" << style;
0512 
0513         //type not defined by default
0514         //try to use style.
0515         KoXmlElement* draw = stylesReader.drawStyles("hatch").value(style);
0516         if (draw) {
0517             debugOdf << "We have a style";
0518             int angle = 0;
0519             if (draw->hasAttributeNS(KoXmlNS::draw, "rotation")) {
0520                 angle = (draw->attributeNS(KoXmlNS::draw, "rotation", QString()).toInt()) / 10;
0521                 debugOdf << "angle :" << angle;
0522             }
0523             if (draw->hasAttributeNS(KoXmlNS::draw, "color")) {
0524                 //debugOdf<<" draw:color :"<<draw->attributeNS( KoXmlNS::draw,"color", QString() );
0525                 tmpBrush.setColor(draw->attributeNS(KoXmlNS::draw, "color", QString()));
0526             }
0527             if (draw->hasAttributeNS(KoXmlNS::draw, "distance")) {
0528                 //TODO: implement it into Stage
0529             }
0530             if (draw->hasAttributeNS(KoXmlNS::draw, "display-name")) {
0531                 //TODO: implement it into Stage
0532             }
0533             if (draw->hasAttributeNS(KoXmlNS::draw, "style")) {
0534                 //TODO: implement it into Stage
0535                 QString styleHash = draw->attributeNS(KoXmlNS::draw, "style", QString());
0536                 if (styleHash == "single") {
0537                     switch (angle) {
0538                     case 0:
0539                     case 180:
0540                         tmpBrush.setStyle(Qt::HorPattern);
0541                         break;
0542                     case 45:
0543                     case 225:
0544                         tmpBrush.setStyle(Qt::BDiagPattern);
0545                         break;
0546                     case 90:
0547                     case 270:
0548                         tmpBrush.setStyle(Qt::VerPattern);
0549                         break;
0550                     case 135:
0551                     case 315:
0552                         tmpBrush.setStyle(Qt::FDiagPattern);
0553                         break;
0554                     default:
0555                         //todo fixme when we will have a kopaint
0556                         debugOdf << " draw:rotation 'angle' :" << angle;
0557                         break;
0558                     }
0559                 } else if (styleHash == "double") {
0560                     switch (angle) {
0561                     case 0:
0562                     case 180:
0563                     case 90:
0564                     case 270:
0565                         tmpBrush.setStyle(Qt::CrossPattern);
0566                         break;
0567                     case 45:
0568                     case 135:
0569                     case 225:
0570                     case 315:
0571                         tmpBrush.setStyle(Qt::DiagCrossPattern);
0572                         break;
0573                     default:
0574                         //todo fixme when we will have a kopaint
0575                         debugOdf << " draw:rotation 'angle' :" << angle;
0576                         break;
0577                     }
0578 
0579                 } else if (styleHash == "triple") {
0580                     debugOdf << " it is not implemented :(";
0581                 }
0582             }
0583         }
0584     }
0585 
0586     return tmpBrush;
0587 }
0588 
0589 static qreal parseDashEntrySize(QString& attr, qreal penWidth, qreal defaultValue = 0.0){
0590     qreal result = defaultValue;
0591     if (attr.endsWith('%')) {
0592         bool ok;
0593         const int percent = attr.remove('%').toInt(&ok);
0594         if (ok && percent >= 0) {
0595             result = percent / 100.0;
0596         }
0597     } else {
0598         result = KoUnit::parseValue(attr) / penWidth;
0599     }
0600     return result;
0601 }
0602 
0603 QPen KoOdfGraphicStyles::loadOdfStrokeStyle(const KoStyleStack &styleStack, const QString & stroke, const KoOdfStylesReader & stylesReader)
0604 {
0605     QPen tmpPen(Qt::NoPen); // default pen for "none" is a Qt::NoPen
0606 
0607     if (stroke == "solid" || stroke == "dash") {
0608         // If solid or dash is set then we assume that the color is black and the penWidth
0609         // is zero till defined otherwise with the following attributes.
0610         tmpPen = QPen();
0611 
0612         if (styleStack.hasProperty(KoXmlNS::svg, "stroke-color"))
0613             tmpPen.setColor(styleStack.property(KoXmlNS::svg, "stroke-color"));
0614         if (styleStack.hasProperty(KoXmlNS::svg, "stroke-opacity")) {
0615             QColor color = tmpPen.color();
0616             QString opacity = styleStack.property(KoXmlNS::svg, "stroke-opacity");
0617             if (opacity.endsWith('%'))
0618                 color.setAlphaF(0.01 * opacity.remove('%').toDouble());
0619             else
0620                 color.setAlphaF(opacity.toDouble());
0621             tmpPen.setColor(color);
0622         }
0623         if (styleStack.hasProperty(KoXmlNS::svg, "stroke-width"))
0624             tmpPen.setWidthF(KoUnit::parseValue(styleStack.property(KoXmlNS::svg, "stroke-width")));
0625         if (styleStack.hasProperty(KoXmlNS::draw, "stroke-linejoin")) {
0626             QString join = styleStack.property(KoXmlNS::draw, "stroke-linejoin");
0627             if (join == "bevel")
0628                 tmpPen.setJoinStyle(Qt::BevelJoin);
0629             else if (join == "round")
0630                 tmpPen.setJoinStyle(Qt::RoundJoin);
0631             else {
0632                 tmpPen.setJoinStyle(Qt::MiterJoin);
0633                 if (styleStack.hasProperty(KoXmlNS::calligra, "stroke-miterlimit")) {
0634                     QString miterLimit = styleStack.property(KoXmlNS::calligra, "stroke-miterlimit");
0635                     tmpPen.setMiterLimit(miterLimit.toDouble());
0636                 }
0637             }
0638         }
0639         if (styleStack.hasProperty(KoXmlNS::svg, "stroke-linecap")) {
0640             const QString cap = styleStack.property(KoXmlNS::svg, "stroke-linecap");
0641             if (cap == "round")
0642                 tmpPen.setCapStyle(Qt::RoundCap);
0643             else if (cap == "square")
0644                 tmpPen.setCapStyle(Qt::SquareCap);
0645             else
0646                 tmpPen.setCapStyle(Qt::FlatCap);
0647         } else {
0648             // default as per svg specification
0649             tmpPen.setCapStyle(Qt::FlatCap);
0650         }
0651 
0652         if (stroke == "dash" && styleStack.hasProperty(KoXmlNS::draw, "stroke-dash")) {
0653             QString dashStyleName = styleStack.property(KoXmlNS::draw, "stroke-dash");
0654 
0655             // set width to 1 in case it is 0 as dividing by 0 gives infinity
0656             qreal width = tmpPen.widthF();
0657             if ( width == 0 ) {
0658                 width = 1;
0659             }
0660 
0661             KoXmlElement * dashElement = stylesReader.drawStyles("stroke-dash").value(dashStyleName);
0662             if (dashElement) {
0663                 QVector<qreal> dashes;
0664                 if (dashElement->hasAttributeNS(KoXmlNS::draw, "dots1")) {
0665                     QString distance( dashElement->attributeNS(KoXmlNS::draw, "distance", QString()) );
0666                     qreal space = parseDashEntrySize(distance, width, 0.0);
0667 
0668                     QString dots1Length(dashElement->attributeNS(KoXmlNS::draw, "dots1-length", QString()));
0669                     qreal dot1Length = parseDashEntrySize(dots1Length,width,1.0);
0670 
0671                     bool ok;
0672                     int dots1 = dashElement->attributeNS(KoXmlNS::draw, "dots1").toInt(&ok);
0673                     if (!ok) {
0674                         dots1 = 1;
0675                     }
0676 
0677                     for (int i = 0; i < dots1; i++) {
0678                         dashes.append(dot1Length);
0679                         dashes.append(space);
0680                     }
0681 
0682                     if (dashElement->hasAttributeNS(KoXmlNS::draw, "dots2")) {
0683                         QString dots2Length(dashElement->attributeNS(KoXmlNS::draw, "dots2-length", QString()));
0684                         qreal dot2Length = parseDashEntrySize(dots2Length,width,1.0);
0685 
0686                         int dots2 = dashElement->attributeNS(KoXmlNS::draw, "dots2").toInt(&ok);
0687                         if (!ok) {
0688                             dots2 = 1;
0689                         }
0690 
0691                         for (int i = 0; i < dots2; i++) {
0692                             dashes.append(dot2Length);
0693                             dashes.append(space);
0694                         }
0695                     }
0696                     tmpPen.setDashPattern(dashes);
0697                 }
0698             }
0699         }
0700     }
0701 
0702     return tmpPen;
0703 }
0704 
0705 QTransform KoOdfGraphicStyles::loadTransformation(const QString &transformation)
0706 {
0707     QTransform transform;
0708 
0709     // Split string for handling 1 transform statement at a time
0710     QStringList subtransforms = transformation.split(')', QString::SkipEmptyParts);
0711     QStringList::ConstIterator it = subtransforms.constBegin();
0712     QStringList::ConstIterator end = subtransforms.constEnd();
0713     for (; it != end; ++it) {
0714         QStringList subtransform = (*it).split('(', QString::SkipEmptyParts);
0715 
0716         subtransform[0] = subtransform[0].trimmed().toLower();
0717         subtransform[1] = subtransform[1].simplified();
0718         QRegExp reg("[,( ]");
0719         QStringList params = subtransform[1].split(reg, QString::SkipEmptyParts);
0720 
0721         if (subtransform[0].startsWith(';') || subtransform[0].startsWith(','))
0722             subtransform[0] = subtransform[0].right(subtransform[0].length() - 1);
0723 
0724         if (subtransform[0] == "rotate") {
0725             // TODO find out what oo2 really does when rotating, it seems severely broken
0726             if (params.count() == 3) {
0727                 qreal x = KoUnit::parseValue(params[1]);
0728                 qreal y = KoUnit::parseValue(params[2]);
0729 
0730                 transform.translate(x, y);
0731                 // oo2 rotates by radians
0732                 transform.rotate(params[0].toDouble()*180.0 / M_PI);
0733                 transform.translate(-x, -y);
0734             } else {
0735                 // oo2 rotates by radians
0736                 transform.rotate(params[0].toDouble()*180.0 / M_PI);
0737             }
0738         } else if (subtransform[0] == "translate") {
0739             if (params.count() == 2) {
0740                 qreal x = KoUnit::parseValue(params[0]);
0741                 qreal y = KoUnit::parseValue(params[1]);
0742                 transform.translate(x, y);
0743             } else   // Spec : if only one param given, assume 2nd param to be 0
0744                 transform.translate(KoUnit::parseValue(params[0]) , 0);
0745         } else if (subtransform[0] == "scale") {
0746             if (params.count() == 2)
0747                 transform.scale(params[0].toDouble(), params[1].toDouble());
0748             else    // Spec : if only one param given, assume uniform scaling
0749                 transform.scale(params[0].toDouble(), params[0].toDouble());
0750         } else if (subtransform[0] == "skewx")
0751             transform.shear(tan(params[0].toDouble()), 0.0F);
0752         else if (subtransform[0] == "skewy")
0753             transform.shear(tan(params[0].toDouble()), 0.0F);
0754         else if (subtransform[0] == "matrix") {
0755             if (params.count() >= 6) {
0756                 transform.setMatrix(params[0].toDouble(), params[1].toDouble(), 0,
0757                     params[2].toDouble(), params[3].toDouble(), 0,
0758                     KoUnit::parseValue(params[4]), KoUnit::parseValue(params[5]), 1);
0759             }
0760         }
0761     }
0762 
0763     return transform;
0764 }
0765 
0766 QString KoOdfGraphicStyles::saveTransformation(const QTransform &transformation, bool appendTranslateUnit)
0767 {
0768     QString transform;
0769     if (appendTranslateUnit)
0770         transform = QString("matrix(%1 %2 %3 %4 %5pt %6pt)")
0771                     .arg(transformation.m11()).arg(transformation.m12())
0772                     .arg(transformation.m21()).arg(transformation.m22())
0773                     .arg(transformation.dx()) .arg(transformation.dy());
0774     else
0775         transform = QString("matrix(%1 %2 %3 %4 %5 %6)")
0776                     .arg(transformation.m11()).arg(transformation.m12())
0777                     .arg(transformation.m21()).arg(transformation.m22())
0778                     .arg(transformation.dx()) .arg(transformation.dy());
0779 
0780     return transform;
0781 }
0782