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