1 /* This file is part of the KDE project
2    Copyright (C) 2004-2006 David Faure <faure@kde.org>
3    Copyright (C) 2007 Jan Hambrecht <jaham@gmx.net>
4    Copyright (C) 2007-2008,2010-2011 Thorsten Zachmann <zachmann@kde.org>
5    Copyright (C) 2011 Lukáš Tvrdý <lukas.tvrdy@ixonos.com>
6 
7    This library is free software; you can redistribute it and/or
8    modify it under the terms of the GNU Library General Public
9    License as published by the Free Software Foundation; either
10    version 2 of the License, or (at your option) any later version.
11 
12    This library is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15    Library General Public License for more details.
16 
17    You should have received a copy of the GNU Library General Public License
18    along with this library; see the file COPYING.LIB.  If not, write to
19    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20  * Boston, MA 02110-1301, USA.
21 */
22 
23 #include "KoOdfGraphicStyles.h"
24 
25 #include <QBrush>
26 #include <QBuffer>
27 #include <QPen>
28 
29 #include <OdfDebug.h>
30 
31 #include <KoGenStyles.h>
32 #include <KoStyleStack.h>
33 #include <KoUnit.h>
34 #include <KoXmlNS.h>
35 #include <KoXmlWriter.h>
36 #include <KoXmlReader.h>
37 
38 #include "KoOdfStylesReader.h"
39 
saveOdfFillStyle(KoGenStyle & styleFill,KoGenStyles & mainStyles,const QBrush & brush)40 void KoOdfGraphicStyles::saveOdfFillStyle(KoGenStyle &styleFill, KoGenStyles& mainStyles, const QBrush & brush)
41 {
42     KoGenStyle::Type type = styleFill.type();
43     KoGenStyle::PropertyType propertyType = (type == KoGenStyle::GraphicStyle || type == KoGenStyle::GraphicAutoStyle ||
44                                              type == KoGenStyle::DrawingPageStyle || type == KoGenStyle::DrawingPageAutoStyle )
45                                             ? KoGenStyle::DefaultType : KoGenStyle::GraphicType;
46     switch (brush.style()) {
47     case Qt::Dense1Pattern:
48         styleFill.addProperty("draw:opacity", "6%", propertyType);
49         styleFill.addProperty("draw:fill", "solid", propertyType);
50         styleFill.addProperty("draw:fill-color", brush.color().name(), propertyType);
51         break;
52     case Qt::Dense2Pattern:
53         styleFill.addProperty("draw:opacity", "12%", propertyType);
54         styleFill.addProperty("draw:fill", "solid", propertyType);
55         styleFill.addProperty("draw:fill-color", brush.color().name(), propertyType);
56         break;
57     case Qt::Dense3Pattern:
58         styleFill.addProperty("draw:opacity", "37%", propertyType);
59         styleFill.addProperty("draw:fill", "solid", propertyType);
60         styleFill.addProperty("draw:fill-color", brush.color().name(), propertyType);
61         break;
62     case Qt::Dense4Pattern:
63         styleFill.addProperty("draw:opacity", "50%", propertyType);
64         styleFill.addProperty("draw:fill", "solid", propertyType);
65         styleFill.addProperty("draw:fill-color", brush.color().name(), propertyType);
66         break;
67     case Qt::Dense5Pattern:
68         styleFill.addProperty("draw:opacity", "63%", propertyType);
69         styleFill.addProperty("draw:fill", "solid", propertyType);
70         styleFill.addProperty("draw:fill-color", brush.color().name(), propertyType);
71         break;
72     case Qt::Dense6Pattern:
73         styleFill.addProperty("draw:opacity", "88%", propertyType);
74         styleFill.addProperty("draw:fill", "solid", propertyType);
75         styleFill.addProperty("draw:fill-color", brush.color().name(), propertyType);
76         break;
77     case Qt::Dense7Pattern:
78         styleFill.addProperty("draw:opacity", "94%", propertyType);
79         styleFill.addProperty("draw:fill", "solid", propertyType);
80         styleFill.addProperty("draw:fill-color", brush.color().name(), propertyType);
81         break;
82     case Qt::LinearGradientPattern:
83     case Qt::RadialGradientPattern:
84     case Qt::ConicalGradientPattern:
85         styleFill.addProperty("draw:fill", "gradient", propertyType);
86         styleFill.addProperty("draw:fill-gradient-name", saveOdfGradientStyle(mainStyles, brush), propertyType);
87         break;
88     case Qt::HorPattern:
89     case Qt::VerPattern:
90     case Qt::CrossPattern:
91     case Qt::BDiagPattern:
92     case Qt::FDiagPattern:
93     case Qt::DiagCrossPattern:
94         styleFill.addProperty("draw:fill", "hatch", propertyType);
95         styleFill.addProperty("draw:fill-hatch-name", saveOdfHatchStyle(mainStyles, brush), propertyType);
96         break;
97     case Qt::SolidPattern:
98         styleFill.addProperty("draw:fill", "solid", propertyType);
99         styleFill.addProperty("draw:fill-color", brush.color().name(), propertyType);
100         if (! brush.isOpaque())
101             styleFill.addProperty("draw:opacity", QString("%1%").arg(brush.color().alphaF() * 100.0), propertyType);
102         break;
103     case Qt::NoBrush:
104     default:
105         styleFill.addProperty("draw:fill", "none", propertyType);
106         break;
107     }
108 }
109 
saveOdfStrokeStyle(KoGenStyle & styleStroke,KoGenStyles & mainStyles,const QPen & pen)110 void KoOdfGraphicStyles::saveOdfStrokeStyle(KoGenStyle &styleStroke, KoGenStyles &mainStyles, const QPen &pen)
111 {
112     // TODO implement all possibilities
113     switch (pen.style()) {
114     case Qt::NoPen:
115         styleStroke.addProperty("draw:stroke", "none", KoGenStyle::GraphicType);
116         return;
117     case Qt::SolidLine:
118         styleStroke.addProperty("draw:stroke", "solid", KoGenStyle::GraphicType);
119         break;
120     default: { // must be a dashed line
121         styleStroke.addProperty("draw:stroke", "dash", KoGenStyle::GraphicType);
122         // save stroke dash (14.14.7) which is severely limited, but still
123         KoGenStyle dashStyle(KoGenStyle::StrokeDashStyle);
124         dashStyle.addAttribute("draw:style", "rect");
125         QVector<qreal> dashes = pen.dashPattern();
126         dashStyle.addAttribute("draw:dots1", static_cast<int>(1));
127         dashStyle.addAttribute("draw:dots1-length", dashes[0]*pen.widthF());
128         dashStyle.addAttribute("draw:distance", dashes[1]*pen.widthF());
129         if (dashes.size() > 2) {
130             dashStyle.addAttribute("draw:dots2", static_cast<int>(1));
131             dashStyle.addAttribute("draw:dots2-length", dashes[2]*pen.widthF());
132         }
133         QString dashStyleName = mainStyles.insert(dashStyle, "dash");
134         styleStroke.addProperty("draw:stroke-dash", dashStyleName, KoGenStyle::GraphicType);
135         break;
136     }
137     }
138 
139     if (pen.brush().gradient()) {
140         styleStroke.addProperty("calligra:stroke-gradient", saveOdfGradientStyle(mainStyles, pen.brush()), KoGenStyle::GraphicType);
141     }
142     else {
143         styleStroke.addProperty("svg:stroke-color", pen.color().name(), KoGenStyle::GraphicType);
144         styleStroke.addProperty("svg:stroke-opacity", QString("%1").arg(pen.color().alphaF()), KoGenStyle::GraphicType);
145     }
146     styleStroke.addPropertyPt("svg:stroke-width", pen.widthF(), KoGenStyle::GraphicType);
147 
148     switch (pen.joinStyle()) {
149     case Qt::MiterJoin:
150         styleStroke.addProperty("draw:stroke-linejoin", "miter", KoGenStyle::GraphicType);
151         break;
152     case Qt::BevelJoin:
153         styleStroke.addProperty("draw:stroke-linejoin", "bevel", KoGenStyle::GraphicType);
154         break;
155     case Qt::RoundJoin:
156         styleStroke.addProperty("draw:stroke-linejoin", "round", KoGenStyle::GraphicType);
157         break;
158     default:
159         styleStroke.addProperty("draw:stroke-linejoin", "miter", KoGenStyle::GraphicType);
160         styleStroke.addProperty("calligra:stroke-miterlimit", QString("%1").arg(pen.miterLimit()), KoGenStyle::GraphicType);
161         break;
162     }
163     switch (pen.capStyle()) {
164     case Qt::RoundCap:
165         styleStroke.addProperty("svg:stroke-linecap", "round", KoGenStyle::GraphicType);
166         break;
167     case Qt::SquareCap:
168         styleStroke.addProperty("svg:stroke-linecap", "square", KoGenStyle::GraphicType);
169         break;
170     default:
171         styleStroke.addProperty("svg:stroke-linecap", "butt", KoGenStyle::GraphicType);
172         break;
173     }
174 }
175 
saveOdfHatchStyle(KoGenStyles & mainStyles,const QBrush & brush)176 QString KoOdfGraphicStyles::saveOdfHatchStyle(KoGenStyles& mainStyles, const QBrush &brush)
177 {
178     KoGenStyle hatchStyle(KoGenStyle::HatchStyle /*no family name*/);
179     hatchStyle.addAttribute("draw:color", brush.color().name());
180     //hatchStyle.addAttribute( "draw:distance", m_distance ); not implemented into kpresenter
181     switch (brush.style()) {
182     case Qt::HorPattern:
183         hatchStyle.addAttribute("draw:style", "single");
184         hatchStyle.addAttribute("draw:rotation", 0);
185         break;
186     case Qt::BDiagPattern:
187         hatchStyle.addAttribute("draw:style", "single");
188         hatchStyle.addAttribute("draw:rotation", 450);
189         break;
190     case Qt::VerPattern:
191         hatchStyle.addAttribute("draw:style", "single");
192         hatchStyle.addAttribute("draw:rotation", 900);
193         break;
194     case Qt::FDiagPattern:
195         hatchStyle.addAttribute("draw:style", "single");
196         hatchStyle.addAttribute("draw:rotation", 1350);
197         break;
198     case Qt::CrossPattern:
199         hatchStyle.addAttribute("draw:style", "double");
200         hatchStyle.addAttribute("draw:rotation", 0);
201         break;
202     case Qt::DiagCrossPattern:
203         hatchStyle.addAttribute("draw:style", "double");
204         hatchStyle.addAttribute("draw:rotation", 450);
205         break;
206     default:
207         break;
208     }
209 
210     return mainStyles.insert(hatchStyle, "hatch");
211 }
212 
saveOdfGradientStyle(KoGenStyles & mainStyles,const QBrush & brush)213 QString KoOdfGraphicStyles::saveOdfGradientStyle(KoGenStyles &mainStyles, const QBrush &brush)
214 {
215     KoGenStyle gradientStyle;
216     if (brush.style() == Qt::RadialGradientPattern) {
217         const QRadialGradient *gradient = static_cast<const QRadialGradient*>(brush.gradient());
218         gradientStyle = KoGenStyle(KoGenStyle::RadialGradientStyle /*no family name*/);
219         gradientStyle.addAttributePercent("svg:cx", gradient->center().x() * 100);
220         gradientStyle.addAttributePercent("svg:cy", gradient->center().y() * 100);
221         gradientStyle.addAttributePercent("svg:r",  gradient->radius() * 100);
222         gradientStyle.addAttributePercent("svg:fx", gradient->focalPoint().x() * 100);
223         gradientStyle.addAttributePercent("svg:fy", gradient->focalPoint().y() * 100);
224     } else if (brush.style() == Qt::LinearGradientPattern) {
225         const QLinearGradient *gradient = static_cast<const QLinearGradient*>(brush.gradient());
226         gradientStyle = KoGenStyle(KoGenStyle::LinearGradientStyle /*no family name*/);
227         gradientStyle.addAttributePercent("svg:x1", gradient->start().x() * 100);
228         gradientStyle.addAttributePercent("svg:y1", gradient->start().y() * 100);
229         gradientStyle.addAttributePercent("svg:x2", gradient->finalStop().x() * 100);
230         gradientStyle.addAttributePercent("svg:y2", gradient->finalStop().y() * 100);
231     } else if (brush.style() == Qt::ConicalGradientPattern) {
232         const QConicalGradient * gradient = static_cast<const QConicalGradient*>(brush.gradient());
233         gradientStyle = KoGenStyle(KoGenStyle::ConicalGradientStyle /*no family name*/);
234         gradientStyle.addAttributePercent("svg:cx", gradient->center().x() * 100);
235         gradientStyle.addAttributePercent("svg:cy", gradient->center().y() * 100);
236         gradientStyle.addAttribute("draw:angle", QString("%1").arg(gradient->angle()));
237     }
238     const QGradient * gradient = brush.gradient();
239     if (gradient->spread() == QGradient::RepeatSpread)
240         gradientStyle.addAttribute("svg:spreadMethod", "repeat");
241     else if (gradient->spread() == QGradient::ReflectSpread)
242         gradientStyle.addAttribute("svg:spreadMethod", "reflect");
243     else
244         gradientStyle.addAttribute("svg:spreadMethod", "pad");
245 
246     if (! brush.transform().isIdentity()) {
247         gradientStyle.addAttribute("svg:gradientTransform", saveTransformation(brush.transform()));
248     }
249 
250     QBuffer buffer;
251     buffer.open(QIODevice::WriteOnly);
252     KoXmlWriter elementWriter(&buffer);    // TODO pass indentation level
253 
254     // save stops
255     QGradientStops stops = gradient->stops();
256     Q_FOREACH (const QGradientStop & stop, stops) {
257         elementWriter.startElement("svg:stop");
258         elementWriter.addAttribute("svg:offset", QString("%1").arg(stop.first));
259         elementWriter.addAttribute("svg:stop-color", stop.second.name());
260         if (stop.second.alphaF() < 1.0)
261             elementWriter.addAttribute("svg:stop-opacity", QString("%1").arg(stop.second.alphaF()));
262         elementWriter.endElement();
263     }
264 
265     QString elementContents = QString::fromUtf8(buffer.buffer(), buffer.buffer().size());
266     gradientStyle.addChildElement("svg:stop", elementContents);
267 
268     return mainStyles.insert(gradientStyle, "gradient");
269 }
270 
loadOdfGradientStyle(const KoStyleStack & styleStack,const KoOdfStylesReader & stylesReader,const QSizeF & size)271 QBrush KoOdfGraphicStyles::loadOdfGradientStyle(const KoStyleStack &styleStack, const KoOdfStylesReader & stylesReader, const QSizeF &size)
272 {
273     QString styleName = styleStack.property(KoXmlNS::draw, "fill-gradient-name");
274     return loadOdfGradientStyleByName(stylesReader, styleName, size);
275 }
276 
percent(const KoXmlElement & element,const QString & ns,const QString & type,const QString & defaultValue,qreal absolute)277 qreal percent(const KoXmlElement &element, const QString &ns, const QString &type, const QString &defaultValue, qreal absolute)
278 {
279     qreal tmp = 0.0;
280     QString value = element.attributeNS(ns, type, defaultValue);
281     if (value.indexOf('%') > -1) { // percent value
282         tmp = value.remove('%').toDouble() / 100.0;
283     }
284     else { // fixed value
285         tmp = KoUnit::parseValue(value) / absolute;
286         // The following is done so that we get the same data as when we save/load.
287         // This is needed that we get the same values due to rounding differences
288         // of absolute and relative values.
289         QString value = QString("%1").arg(tmp * 100.0);
290         tmp = value.toDouble() / 100;
291     }
292 
293     return tmp;
294 }
295 
loadOdfGradientStyleByName(const KoOdfStylesReader & stylesReader,const QString & styleName,const QSizeF & size)296 QBrush KoOdfGraphicStyles::loadOdfGradientStyleByName(const KoOdfStylesReader &stylesReader, const QString &styleName, const QSizeF &size)
297 {
298     KoXmlElement* e = stylesReader.drawStyles("gradient")[styleName];
299     if (! e)
300         return QBrush();
301 
302     QGradient * gradient = 0;
303     QTransform transform;
304 
305     if (e->namespaceURI() == KoXmlNS::draw && e->localName() == "gradient") {
306         // FIXME seems like oo renders the gradient start stop color at the center of the
307         // radial gradient, and the start color at the radius of the radial gradient
308         // whereas it is not mentioned in the spec how it should be rendered
309         // note that svg defines that exactly as the opposite as oo does
310         // so what should we do?
311         QString type = e->attributeNS(KoXmlNS::draw, "style", QString());
312         if (type == "radial") {
313             // Zagge: at the moment the only objectBoundingBox is supported:
314             // 18.539 svg:gradientUnits
315             // See §13.2.2 and §13.2.3 of [SVG].
316             // The default value for this attribute is objectBoundingBox.
317             // The only value of the svg:gradientUnits attribute is objectBoundingBox.
318 
319             qreal cx = KoUnit::parseValue(e->attributeNS(KoXmlNS::draw, "cx", QString()).remove('%'));
320             qreal cy = KoUnit::parseValue(e->attributeNS(KoXmlNS::draw, "cy", QString()).remove('%'));
321             gradient = new QRadialGradient(QPointF(cx * 0.01, cy * 0.01), sqrt(0.5));
322             gradient->setCoordinateMode(QGradient::ObjectBoundingMode);
323         } else if (type == "linear" || type == "axial") {
324             QLinearGradient * lg = new QLinearGradient();
325             lg->setCoordinateMode(QGradient::ObjectBoundingMode);
326             // Dividing by 10 here because OOo saves as degree * 10
327             qreal angle = 90 + e->attributeNS(KoXmlNS::draw, "angle", "0").toDouble() / 10;
328             qreal radius = sqrt(0.5);
329 
330             qreal sx = cos(angle * M_PI / 180) * radius;
331             qreal sy = sin(angle * M_PI / 180) * radius;
332             lg->setStart(QPointF(0.5 + sx, 0.5 - sy));
333             lg->setFinalStop(QPointF(0.5 - sx, 0.5 + sy));
334             gradient = lg;
335         } else
336             return QBrush();
337 
338         qreal border = 0.01 * e->attributeNS(KoXmlNS::draw, "border", "0").remove('%').toDouble();
339         QGradientStops stops;
340         if (type != "axial") {
341             // In case of radial gradients the colors are reversed, because OOo saves them as the opposite of the SVG direction
342             // see bug 137639
343             QGradientStop start;
344             start.first = (type != "radial") ? border : 1.0 - border;
345             start.second = QColor(e->attributeNS(KoXmlNS::draw, "start-color", QString()));
346             start.second.setAlphaF(0.01 * e->attributeNS(KoXmlNS::draw, "start-intensity", "100").remove('%').toDouble());
347 
348             QGradientStop end;
349             end.first = (type != "radial") ? 1.0 : 0.0;
350             end.second = QColor(e->attributeNS(KoXmlNS::draw, "end-color", QString()));
351             end.second.setAlphaF(0.01 * e->attributeNS(KoXmlNS::draw, "end-intensity", "100").remove('%').toDouble());
352 
353             stops << start << end;
354         } else {
355             QGradientStop start;
356             start.first = 0.5 * border;
357             start.second = QColor(e->attributeNS(KoXmlNS::draw, "end-color", QString()));
358             start.second.setAlphaF(0.01 * e->attributeNS(KoXmlNS::draw, "end-intensity", "100").remove('%').toDouble());
359 
360             QGradientStop middle;
361             middle.first = 0.5;
362             middle.second = QColor(e->attributeNS(KoXmlNS::draw, "start-color", QString()));
363             middle.second.setAlphaF(0.01 * e->attributeNS(KoXmlNS::draw, "start-intensity", "100").remove('%').toDouble());
364 
365             QGradientStop end;
366             end.first = 1.0 - 0.5 * border;
367             end.second = QColor(e->attributeNS(KoXmlNS::draw, "end-color", QString()));
368             end.second.setAlphaF(0.01 * e->attributeNS(KoXmlNS::draw, "end-intensity", "100").remove('%').toDouble());
369 
370             stops << start << middle << end;
371         }
372 
373         gradient->setStops(stops);
374     } else if (e->namespaceURI() == KoXmlNS::svg) {
375         if (e->localName() == "linearGradient") {
376             QPointF start, stop;
377             start.setX(percent(*e, KoXmlNS::svg, "x1", "0%", size.width()));
378             start.setY(percent(*e, KoXmlNS::svg, "y1", "0%", size.height()));
379             stop.setX(percent(*e, KoXmlNS::svg, "x2", "100%", size.width()));
380             stop.setY(percent(*e, KoXmlNS::svg, "y2", "100%", size.height()));
381             gradient = new QLinearGradient(start, stop);
382         } else if (e->localName() == "radialGradient") {
383             QPointF center, focalPoint;
384             center.setX(percent(*e, KoXmlNS::svg, "cx", "50%", size.width()));
385             center.setY(percent(*e, KoXmlNS::svg, "cy", "50%", size.height()));
386             qreal r = percent(*e, KoXmlNS::svg, "r", "50%", sqrt(size.width() * size.width() + size.height() * size.height()));
387             focalPoint.setX(percent(*e, KoXmlNS::svg, "fx", QString(), size.width()));
388             focalPoint.setY(percent(*e, KoXmlNS::svg, "fy", QString(), size.height()));
389             gradient = new QRadialGradient(center, r, focalPoint );
390         }
391         if (! gradient)
392             return QBrush();
393 
394         gradient->setCoordinateMode(QGradient::ObjectBoundingMode);
395 
396         QString strSpread(e->attributeNS(KoXmlNS::svg, "spreadMethod", "pad"));
397         if (strSpread == "repeat")
398             gradient->setSpread(QGradient::RepeatSpread);
399         else if (strSpread == "reflect")
400             gradient->setSpread(QGradient::ReflectSpread);
401         else
402             gradient->setSpread(QGradient::PadSpread);
403 
404         if (e->hasAttributeNS(KoXmlNS::svg, "gradientTransform"))
405             transform = loadTransformation(e->attributeNS(KoXmlNS::svg, "gradientTransform", QString()));
406 
407         QGradientStops stops;
408 
409         // load stops
410         KoXmlElement colorstop;
411         forEachElement(colorstop, (*e)) {
412             if (colorstop.namespaceURI() == KoXmlNS::svg && colorstop.localName() == "stop") {
413                 QGradientStop stop;
414                 stop.second = QColor(colorstop.attributeNS(KoXmlNS::svg, "stop-color", QString()));
415                 stop.second.setAlphaF(colorstop.attributeNS(KoXmlNS::svg, "stop-opacity", "1.0").toDouble());
416                 stop.first = colorstop.attributeNS(KoXmlNS::svg, "offset", "0.0").toDouble();
417                 stops.append(stop);
418             }
419         }
420         gradient->setStops(stops);
421     } else if (e->namespaceURI() == KoXmlNS::calligra) {
422         if (e->localName() == "conicalGradient") {
423             QPointF center;
424             center.setX(percent(*e, KoXmlNS::svg, "cx", "50%", size.width()));
425             center.setY(percent(*e, KoXmlNS::svg, "cy", "50%", size.height()));
426             qreal angle = KoUnit::parseValue(e->attributeNS(KoXmlNS::draw, "angle", QString()));
427             gradient = new QConicalGradient(center, angle);
428             gradient->setCoordinateMode(QGradient::ObjectBoundingMode);
429 
430             QString strSpread(e->attributeNS(KoXmlNS::svg, "spreadMethod", "pad"));
431             if (strSpread == "repeat")
432                 gradient->setSpread(QGradient::RepeatSpread);
433             else if (strSpread == "reflect")
434                 gradient->setSpread(QGradient::ReflectSpread);
435             else
436                 gradient->setSpread(QGradient::PadSpread);
437 
438             if (e->hasAttributeNS(KoXmlNS::svg, "gradientTransform"))
439                 transform = loadTransformation(e->attributeNS(KoXmlNS::svg, "gradientTransform", QString()));
440 
441             QGradientStops stops;
442 
443             // load stops
444             KoXmlElement colorstop;
445             forEachElement(colorstop, (*e)) {
446                 if (colorstop.namespaceURI() == KoXmlNS::svg && colorstop.localName() == "stop") {
447                     QGradientStop stop;
448                     stop.second = QColor(colorstop.attributeNS(KoXmlNS::svg, "stop-color", QString()));
449                     stop.second.setAlphaF(colorstop.attributeNS(KoXmlNS::svg, "stop-opacity", "1.0").toDouble());
450                     stop.first = colorstop.attributeNS(KoXmlNS::svg, "offset", "0.0").toDouble();
451                     stops.append(stop);
452                 }
453             }
454             gradient->setStops(stops);
455         }
456     }
457 
458     if (! gradient)
459         return QBrush();
460 
461     QBrush resultBrush(*gradient);
462     resultBrush.setTransform(transform);
463 
464     delete gradient;
465     return resultBrush;
466 }
467 
loadOdfFillStyle(const KoStyleStack & styleStack,const QString & fill,const KoOdfStylesReader & stylesReader)468 QBrush KoOdfGraphicStyles::loadOdfFillStyle(const KoStyleStack &styleStack, const QString & fill,  const KoOdfStylesReader & stylesReader)
469 {
470     QBrush tmpBrush; // default brush for "none" is a Qt::NoBrush
471 
472     if (fill == "solid") {
473         tmpBrush.setStyle(Qt::SolidPattern);
474         if (styleStack.hasProperty(KoXmlNS::draw, "fill-color"))
475             tmpBrush.setColor(styleStack.property(KoXmlNS::draw, "fill-color"));
476         if (styleStack.hasProperty(KoXmlNS::draw, "opacity")) {
477             QString opacity = styleStack.property(KoXmlNS::draw, "opacity");
478             if (! opacity.isEmpty() && opacity.right(1) == "%") {
479                 float percent = opacity.left(opacity.length() - 1).toFloat();
480                 QColor color = tmpBrush.color();
481                 color.setAlphaF(percent / 100.0);
482                 tmpBrush.setColor(color);
483             }
484         }
485         //TODO
486         if (styleStack.hasProperty(KoXmlNS::draw, "transparency")) {
487             QString transparency = styleStack.property(KoXmlNS::draw, "transparency");
488             if (transparency == "94%") {
489                 tmpBrush.setStyle(Qt::Dense1Pattern);
490             } else if (transparency == "88%") {
491                 tmpBrush.setStyle(Qt::Dense2Pattern);
492             } else if (transparency == "63%") {
493                 tmpBrush.setStyle(Qt::Dense3Pattern);
494 
495             } else if (transparency == "50%") {
496                 tmpBrush.setStyle(Qt::Dense4Pattern);
497 
498             } else if (transparency == "37%") {
499                 tmpBrush.setStyle(Qt::Dense5Pattern);
500 
501             } else if (transparency == "12%") {
502                 tmpBrush.setStyle(Qt::Dense6Pattern);
503 
504             } else if (transparency == "6%") {
505                 tmpBrush.setStyle(Qt::Dense7Pattern);
506 
507             } else
508                 debugOdf << " transparency is not defined into kpresenter :" << transparency;
509         }
510     } else if (fill == "hatch") {
511         QString style = styleStack.property(KoXmlNS::draw, "fill-hatch-name");
512         debugOdf << " hatch style is  :" << style;
513 
514         //type not defined by default
515         //try to use style.
516         KoXmlElement* draw = stylesReader.drawStyles("hatch")[style];
517         if (draw) {
518             debugOdf << "We have a style";
519             int angle = 0;
520             if (draw->hasAttributeNS(KoXmlNS::draw, "rotation")) {
521                 angle = (draw->attributeNS(KoXmlNS::draw, "rotation", QString()).toInt()) / 10;
522                 debugOdf << "angle :" << angle;
523             }
524             if (draw->hasAttributeNS(KoXmlNS::draw, "color")) {
525                 //debugOdf<<" draw:color :"<<draw->attributeNS( KoXmlNS::draw,"color", QString() );
526                 tmpBrush.setColor(draw->attributeNS(KoXmlNS::draw, "color", QString()));
527             }
528             if (draw->hasAttributeNS(KoXmlNS::draw, "distance")) {
529                 //todo implement it into kpresenter
530             }
531             if (draw->hasAttributeNS(KoXmlNS::draw, "display-name")) {
532                 //todo implement it into kpresenter
533             }
534             if (draw->hasAttributeNS(KoXmlNS::draw, "style")) {
535                 //todo implement it into kpresenter
536                 QString styleHash = draw->attributeNS(KoXmlNS::draw, "style", QString());
537                 if (styleHash == "single") {
538                     switch (angle) {
539                     case 0:
540                     case 180:
541                         tmpBrush.setStyle(Qt::HorPattern);
542                         break;
543                     case 45:
544                     case 225:
545                         tmpBrush.setStyle(Qt::BDiagPattern);
546                         break;
547                     case 90:
548                     case 270:
549                         tmpBrush.setStyle(Qt::VerPattern);
550                         break;
551                     case 135:
552                     case 315:
553                         tmpBrush.setStyle(Qt::FDiagPattern);
554                         break;
555                     default:
556                         //todo fixme when we will have a kopaint
557                         debugOdf << " draw:rotation 'angle' :" << angle;
558                         break;
559                     }
560                 } else if (styleHash == "double") {
561                     switch (angle) {
562                     case 0:
563                     case 180:
564                     case 90:
565                     case 270:
566                         tmpBrush.setStyle(Qt::CrossPattern);
567                         break;
568                     case 45:
569                     case 135:
570                     case 225:
571                     case 315:
572                         tmpBrush.setStyle(Qt::DiagCrossPattern);
573                         break;
574                     default:
575                         //todo fixme when we will have a kopaint
576                         debugOdf << " draw:rotation 'angle' :" << angle;
577                         break;
578                     }
579 
580                 } else if (styleHash == "triple") {
581                     debugOdf << " it is not implemented :(";
582                 }
583             }
584         }
585     }
586 
587     return tmpBrush;
588 }
589 
parseDashEntrySize(QString & attr,qreal penWidth,qreal defaultValue=0.0)590 static qreal parseDashEntrySize(QString& attr, qreal penWidth, qreal defaultValue = 0.0){
591     qreal result = defaultValue;
592     if (attr.endsWith('%')) {
593         bool ok;
594         const int percent = attr.remove('%').toInt(&ok);
595         if (ok && percent >= 0) {
596             result = percent / 100.0;
597         }
598     } else {
599         result = KoUnit::parseValue(attr) / penWidth;
600     }
601     return result;
602 }
603 
loadOdfStrokeStyle(const KoStyleStack & styleStack,const QString & stroke,const KoOdfStylesReader & stylesReader)604 QPen KoOdfGraphicStyles::loadOdfStrokeStyle(const KoStyleStack &styleStack, const QString & stroke, const KoOdfStylesReader & stylesReader)
605 {
606     QPen tmpPen(Qt::NoPen); // default pen for "none" is a Qt::NoPen
607 
608     if (stroke == "solid" || stroke == "dash") {
609         // If solid or dash is set then we assume that the color is black and the penWidth
610         // is zero till defined otherwise with the following attributes.
611         tmpPen = QPen();
612 
613         if (styleStack.hasProperty(KoXmlNS::svg, "stroke-color"))
614             tmpPen.setColor(styleStack.property(KoXmlNS::svg, "stroke-color"));
615         if (styleStack.hasProperty(KoXmlNS::svg, "stroke-opacity")) {
616             QColor color = tmpPen.color();
617             QString opacity = styleStack.property(KoXmlNS::svg, "stroke-opacity");
618             if (opacity.endsWith('%'))
619                 color.setAlphaF(0.01 * opacity.remove('%').toDouble());
620             else
621                 color.setAlphaF(opacity.toDouble());
622             tmpPen.setColor(color);
623         }
624         if (styleStack.hasProperty(KoXmlNS::svg, "stroke-width"))
625             tmpPen.setWidthF(KoUnit::parseValue(styleStack.property(KoXmlNS::svg, "stroke-width")));
626         if (styleStack.hasProperty(KoXmlNS::draw, "stroke-linejoin")) {
627             QString join = styleStack.property(KoXmlNS::draw, "stroke-linejoin");
628             if (join == "bevel")
629                 tmpPen.setJoinStyle(Qt::BevelJoin);
630             else if (join == "round")
631                 tmpPen.setJoinStyle(Qt::RoundJoin);
632             else {
633                 tmpPen.setJoinStyle(Qt::MiterJoin);
634                 if (styleStack.hasProperty(KoXmlNS::calligra, "stroke-miterlimit")) {
635                     QString miterLimit = styleStack.property(KoXmlNS::calligra, "stroke-miterlimit");
636                     tmpPen.setMiterLimit(miterLimit.toDouble());
637                 }
638             }
639         }
640         if (styleStack.hasProperty(KoXmlNS::svg, "stroke-linecap")) {
641             const QString cap = styleStack.property(KoXmlNS::svg, "stroke-linecap");
642             if (cap == "round")
643                 tmpPen.setCapStyle(Qt::RoundCap);
644             else if (cap == "square")
645                 tmpPen.setCapStyle(Qt::SquareCap);
646             else
647                 tmpPen.setCapStyle(Qt::FlatCap);
648         } else {
649             // default as per svg specification
650             tmpPen.setCapStyle(Qt::FlatCap);
651         }
652 
653         if (stroke == "dash" && styleStack.hasProperty(KoXmlNS::draw, "stroke-dash")) {
654             QString dashStyleName = styleStack.property(KoXmlNS::draw, "stroke-dash");
655 
656             // set width to 1 in case it is 0 as dividing by 0 gives infinity
657             qreal width = tmpPen.widthF();
658             if ( width == 0 ) {
659                 width = 1;
660             }
661 
662             KoXmlElement * dashElement = stylesReader.drawStyles("stroke-dash")[ dashStyleName ];
663             if (dashElement) {
664                 QVector<qreal> dashes;
665                 if (dashElement->hasAttributeNS(KoXmlNS::draw, "dots1")) {
666                     QString distance( dashElement->attributeNS(KoXmlNS::draw, "distance", QString()) );
667                     qreal space = parseDashEntrySize(distance, width, 0.0);
668 
669                     QString dots1Length(dashElement->attributeNS(KoXmlNS::draw, "dots1-length", QString()));
670                     qreal dot1Length = parseDashEntrySize(dots1Length,width,1.0);
671 
672                     bool ok;
673                     int dots1 = dashElement->attributeNS(KoXmlNS::draw, "dots1").toInt(&ok);
674                     if (!ok) {
675                         dots1 = 1;
676                     }
677 
678                     for (int i = 0; i < dots1; i++) {
679                         dashes.append(dot1Length);
680                         dashes.append(space);
681                     }
682 
683                     if (dashElement->hasAttributeNS(KoXmlNS::draw, "dots2")) {
684                         QString dots2Length(dashElement->attributeNS(KoXmlNS::draw, "dots2-length", QString()));
685                         qreal dot2Length = parseDashEntrySize(dots2Length,width,1.0);
686 
687                         int dots2 = dashElement->attributeNS(KoXmlNS::draw, "dots2").toInt(&ok);
688                         if (!ok) {
689                             dots2 = 1;
690                         }
691 
692                         for (int i = 0; i < dots2; i++) {
693                             dashes.append(dot2Length);
694                             dashes.append(space);
695                         }
696                     }
697                     tmpPen.setDashPattern(dashes);
698                 }
699             }
700         }
701     }
702 
703     return tmpPen;
704 }
705 
loadTransformation(const QString & transformation)706 QTransform KoOdfGraphicStyles::loadTransformation(const QString &transformation)
707 {
708     QTransform transform;
709 
710     // Split string for handling 1 transform statement at a time
711     QStringList subtransforms = transformation.split(')', QString::SkipEmptyParts);
712     QStringList::ConstIterator it = subtransforms.constBegin();
713     QStringList::ConstIterator end = subtransforms.constEnd();
714     for (; it != end; ++it) {
715         QStringList subtransform = (*it).split('(', QString::SkipEmptyParts);
716 
717         subtransform[0] = subtransform[0].trimmed().toLower();
718         subtransform[1] = subtransform[1].simplified();
719         QRegExp reg("[,( ]");
720         QStringList params = subtransform[1].split(reg, QString::SkipEmptyParts);
721 
722         if (subtransform[0].startsWith(';') || subtransform[0].startsWith(','))
723             subtransform[0] = subtransform[0].right(subtransform[0].length() - 1);
724 
725         if (subtransform[0] == "rotate") {
726             // TODO find out what oo2 really does when rotating, it seems severely broken
727             if (params.count() == 3) {
728                 qreal x = KoUnit::parseValue(params[1]);
729                 qreal y = KoUnit::parseValue(params[2]);
730 
731                 transform.translate(x, y);
732                 // oo2 rotates by radians
733                 transform.rotate(params[0].toDouble()*180.0 / M_PI);
734                 transform.translate(-x, -y);
735             } else {
736                 // oo2 rotates by radians
737                 transform.rotate(params[0].toDouble()*180.0 / M_PI);
738             }
739         } else if (subtransform[0] == "translate") {
740             if (params.count() == 2) {
741                 qreal x = KoUnit::parseValue(params[0]);
742                 qreal y = KoUnit::parseValue(params[1]);
743                 transform.translate(x, y);
744             } else   // Spec : if only one param given, assume 2nd param to be 0
745                 transform.translate(KoUnit::parseValue(params[0]) , 0);
746         } else if (subtransform[0] == "scale") {
747             if (params.count() == 2)
748                 transform.scale(params[0].toDouble(), params[1].toDouble());
749             else    // Spec : if only one param given, assume uniform scaling
750                 transform.scale(params[0].toDouble(), params[0].toDouble());
751         } else if (subtransform[0] == "skewx")
752             transform.shear(tan(params[0].toDouble()), 0.0F);
753         else if (subtransform[0] == "skewy")
754             transform.shear(tan(params[0].toDouble()), 0.0F);
755         else if (subtransform[0] == "matrix") {
756             if (params.count() >= 6) {
757                 transform.setMatrix(params[0].toDouble(), params[1].toDouble(), 0,
758                     params[2].toDouble(), params[3].toDouble(), 0,
759                     KoUnit::parseValue(params[4]), KoUnit::parseValue(params[5]), 1);
760             }
761         }
762     }
763 
764     return transform;
765 }
766 
saveTransformation(const QTransform & transformation,bool appendTranslateUnit)767 QString KoOdfGraphicStyles::saveTransformation(const QTransform &transformation, bool appendTranslateUnit)
768 {
769     QString transform;
770     if (appendTranslateUnit)
771         transform = QString("matrix(%1 %2 %3 %4 %5pt %6pt)")
772                     .arg(transformation.m11()).arg(transformation.m12())
773                     .arg(transformation.m21()).arg(transformation.m22())
774                     .arg(transformation.dx()) .arg(transformation.dy());
775     else
776         transform = QString("matrix(%1 %2 %3 %4 %5 %6)")
777                     .arg(transformation.m11()).arg(transformation.m12())
778                     .arg(transformation.m21()).arg(transformation.m22())
779                     .arg(transformation.dx()) .arg(transformation.dy());
780 
781     return transform;
782 }
783 
784