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