KReport

KReportUtils.cpp
1/* This file is part of the KDE project
2 Copyright (C) 2010-2015 Jarosław Staniek <staniek@kde.org>
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License as published by the Free Software Foundation; either
7 version 2 of the License, or (at your option) any later version.
8
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to
16 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18*/
19
20#include "KReportUtils.h"
21#include "KReportUnit.h"
22#include "KReportItemBase.h"
23#include "KReportLineStyle.h"
24
25#include <KProperty>
26
27#include <QDomDocument>
28#include <QDomElement>
29
30#include <float.h>
31
32QString KReportUtils::attr(const QDomElement &el, const QString &attrName,
33 const QString &defaultValue)
34{
35 const QString val = el.attribute(attrName);
36 return val.isEmpty() ? defaultValue : val;
37}
38
39QByteArray KReportUtils::attr(const QDomElement &el, const QString &attrName,
40 const QByteArray &defaultValue)
41{
42 const QByteArray val = el.attribute(attrName).toLatin1();
43 return val.isEmpty() ? defaultValue : val;
44}
45
46bool KReportUtils::attr(const QDomElement &el, const QString &attrName, bool defaultValue)
47{
48 const QString val = el.attribute(attrName);
49 return val.isEmpty() ? defaultValue : QVariant(val).toBool();
50}
51
52int KReportUtils::attr(const QDomElement &el, const QString &attrName, int defaultValue)
53{
54 const QString val = el.attribute(attrName);
55 if (val.isEmpty()) {
56 return defaultValue;
57 }
58 bool ok;
59 const int result = QVariant(val).toInt(&ok);
60 return ok ? result : defaultValue;
61}
62
63qreal KReportUtils::attr(const QDomElement &el, const QString &attrName, qreal defaultValue)
64{
65 const QString val = el.attribute(attrName);
66 return KReportUnit::parseValue(val, defaultValue);
67}
68
69QColor KReportUtils::attr(const QDomElement &el, const QString &attrName, const QColor &defaultValue)
70{
71 const QString val = el.attribute(attrName);
72 if (val.isEmpty()) {
73 return defaultValue;
74 }
75 return QColor(val);
76}
77
78qreal KReportUtils::attrPercent(const QDomElement& el, const QString &attrName, qreal defaultValue)
79{
80 QString str(el.attribute(attrName));
81 if (str.isEmpty() || !str.endsWith(QLatin1Char('%'))) {
82 return defaultValue;
83 }
84 str.chop(1);
85 bool ok;
86 const qreal result = QVariant(str).toReal(&ok) / 100.0;
87 if (!ok) {
88 return defaultValue;
89 }
90 return result;
91}
92
93Qt::PenStyle KReportUtils::penStyle(const QString& str, Qt::PenStyle defaultValue)
94{
95 const QByteArray s(str.toLatin1());
96 if (s == "nopen" || s == "none") {
97 return Qt::NoPen;
98 } else if (s == "solid") {
99 return Qt::SolidLine;
100 } else if (s == "dash" || s == "wave" /*we have nothing better for now*/) {
101 return Qt::DashLine;
102 } else if (s == "dot" || s == "dotted") {
103 return Qt::DotLine;
104 } else if (s == "dashdot" || s == "dot-dash") {
105 return Qt::DashDotLine;
106 } else if (s == "dashdotdot" || s == "dot-dot-dash") {
107 return Qt::DashDotDotLine;
108 } else {
109 return defaultValue;
110 }
111}
112
113Qt::Alignment KReportUtils::verticalAlignment(const QString &str, Qt::Alignment defaultValue)
114{
115 const QByteArray s(str.toLatin1());
116 if (s == "center") {
117 return Qt::AlignVCenter;
118 } else if (s == "top") {
119 return Qt::AlignTop;
120 } else if (s == "bottom") {
121 return Qt::AlignBottom;
122 } else {
123 return defaultValue;
124 }
125}
126
127Qt::Alignment KReportUtils::horizontalAlignment(const QString &str, Qt::Alignment defaultValue)
128{
129 const QByteArray s(str.toLatin1());
130 if (s == "center") {
131 return Qt::AlignHCenter;
132 } else if (s == "right") {
133 return Qt::AlignRight;
134 } else if (s == "left") {
135 return Qt::AlignLeft;
136 } else {
137 return defaultValue;
138 }
139}
140
141QString KReportUtils::verticalToString(Qt::Alignment alignment)
142{
143 if (alignment.testFlag(Qt::AlignVCenter)) {
144 return QLatin1String("center");
145 } else if (alignment.testFlag(Qt::AlignTop)) {
146 return QLatin1String("top");
147 } else if (alignment.testFlag(Qt::AlignBottom)) {
148 return QLatin1String("bottom");
149 }
150 return QString();
151}
152
153QString KReportUtils::horizontalToString(Qt::Alignment alignment)
154{
155 if (alignment.testFlag(Qt::AlignHCenter)) {
156 return QLatin1String("center");
157 } else if (alignment.testFlag(Qt::AlignLeft)) {
158 return QLatin1String("left");
159 } else if (alignment.testFlag(Qt::AlignRight)) {
160 return QLatin1String("right");
161 }
162 return QString();
163}
164
165QString KReportUtils::readNameAttribute(const QDomElement &el, const QString &defaultValue)
166{
167 return attr(el, QLatin1String("report:name"), defaultValue);
168}
169
170QSizeF KReportUtils::readSizeAttributes(const QDomElement &el, const QSizeF &defaultValue)
171{
172 QSizeF val;
173 val.setWidth(attr(el, QLatin1String("svg:width"), defaultValue.width()));
174 if (val.width() < 0.0) {
175 val.setWidth(defaultValue.width());
176 }
177 val.setHeight(attr(el, QLatin1String("svg:height"), defaultValue.height()));
178 if (val.height() < 0.0) {
179 val.setHeight(defaultValue.height());
180 }
181 return val;
182}
183
184QRectF KReportUtils::readRectAttributes(const QDomElement &el, const QRectF &defaultValue)
185{
186 QRectF val;
187 val.setX(attr(el, QLatin1String("svg:x"), defaultValue.x()));
188 val.setY(attr(el, QLatin1String("svg:y"), defaultValue.y()));
189 val.setSize(readSizeAttributes(el, defaultValue.size()));
190 return val;
191}
192
193qreal KReportUtils::readZAttribute(const QDomElement &el, qreal defaultValue)
194{
195 return KReportUtils::attr(el, QLatin1String("report:z-index"), defaultValue);
196}
197
198int KReportUtils::readPercent(const QDomElement& el, const QString &attrName, int defaultPercentValue, bool *ok)
199{
200 QString percent(el.attribute(attrName));
201 if (percent.isEmpty()) {
202 if (ok)
203 *ok = true;
204 return defaultPercentValue;
205 }
206 if (!percent.endsWith(QLatin1Char('%'))) {
207 if (ok)
208 *ok = false;
209 return 0;
210 }
211 percent.chop(1);
212 if (ok)
213 *ok = true;
214 return percent.toInt(ok);
215}
216
217QString KReportUtils::readSectionTypeNameAttribute(const QDomElement &el, const QString &defaultValue)
218{
219 return attr(el, QLatin1String("report:section-type"), defaultValue);
220}
221
222//! @return string representation of @a value, cuts of zeros; precision is set to 2
223static QString roundValueToString(qreal value)
224{
225 QString s(QString::number(value, 'g', 2));
226 if (s.endsWith(QLatin1String(".00")))
227 return QString::number(qRound(value));
228 return s;
229}
230
231//! Used by readFontAttributes()
232static QFont::Capitalization readFontCapitalization(const QByteArray& fontVariant, const QByteArray& textTransform)
233{
234 if (fontVariant == "small-caps")
235 return QFont::SmallCaps;
236 if (textTransform == "uppercase")
237 return QFont::AllUppercase;
238 if (textTransform == "lowercase")
239 return QFont::AllLowercase;
240 if (textTransform == "capitalize")
241 return QFont::Capitalize;
242 // default, "normal"
243 return QFont::MixedCase;
244}
245
246void KReportUtils::readFontAttributes(const QDomElement& el, QFont *font)
247{
248 Q_ASSERT(font);
249 const QFont::Capitalization cap = readFontCapitalization(
250 attr(el, QLatin1String("fo:font-variant"), QByteArray()),
251 attr(el, QLatin1String("fo:text-transform"), QByteArray()));
252 font->setCapitalization(cap);
253
254 // weight
255 const QByteArray fontWeight(attr(el, QLatin1String("fo:font-weight"), QByteArray()));
256 int weight = -1;
257 if (fontWeight == "bold") {
258 weight = QFont::Bold;
259 }
260 if (fontWeight == "normal") {
261 weight = QFont::Normal;
262 }
263 else if (!fontWeight.isEmpty()) {
264 // Remember : Qt and CSS/XSL doesn't have the same scale. It's 100-900 instead of Qt's 0-100
265 // See http://www.w3.org/TR/2001/REC-xsl-20011015/slice7.html#font-weight
266 // and http://www.w3.org/TR/CSS2/fonts.html#font-boldness
267 bool ok;
268 qreal boldness = fontWeight.toUInt(&ok);
269 if (ok) {
270 boldness = qMin(boldness, 900.0);
271 boldness = qMax(boldness, 100.0);
272 weight = (boldness - 100.0) * 0.12375 /*== 99/800*/; // 0..99
273 }
274 }
275 if (weight >= 0) {
276 font->setWeight(weight);
277 }
278
279 font->setItalic(attr(el, QLatin1String("fo:font-style"), QByteArray()) == "italic");
280 font->setFixedPitch(attr(el, QLatin1String("style:font-pitch"), QByteArray()) == "fixed");
281 font->setFamily(attr(el, QLatin1String("fo:font-family"), font->family()));
282 font->setKerning(attr(el, QLatin1String("style:letter-kerning"), font->kerning()));
283
284 // underline
285 const QByteArray underlineType(
286 attr(el, QLatin1String("style:text-underline-type"), QByteArray()));
287 font->setUnderline(!underlineType.isEmpty()
288 && underlineType
289 != "none"); // double or single (we don't recognize them)
290
291 // stricken-out
292 const QByteArray strikeOutType(attr(el, QLatin1String("style:text-line-through-type"), QByteArray()));
293 font->setStrikeOut(!strikeOutType.isEmpty() && strikeOutType != "none"); // double or single (we don't recognize them)
294
295//! @todo support fo:font-size-rel?
296//! @todo support fo:font-size in px
297 font->setPointSizeF(KReportUtils::attr(el, QLatin1String("fo:font-size"), font->pointSizeF()));
298
299 // letter spacing
300 // §7.16.2 of [XSL] http://www.w3.org/TR/xsl11/#letter-spacing
302 100.0 * KReportUtils::attrPercent(
303 el, QLatin1String("fo:letter-spacing"), font->letterSpacing()));
304}
305
306void KReportUtils::writeFontAttributes(QDomElement *el, const QFont &font)
307{
308 Q_ASSERT(el);
309 switch (font.capitalization()) {
310 case QFont::SmallCaps:
311 el->setAttribute(QLatin1String("fo:font-variant"), QLatin1String("small-caps"));
312 break;
313 case QFont::MixedCase:
314 // default: "normal", do not save
315 break;
317 el->setAttribute(QLatin1String("fo:text-transform"), QLatin1String("uppercase"));
318 break;
320 el->setAttribute(QLatin1String("fo:text-transform"), QLatin1String("lowercase"));
321 break;
323 el->setAttribute(QLatin1String("fo:text-transform"), QLatin1String("capitalize"));
324 break;
325 }
326
327 // Remember : Qt and CSS/XSL doesn't have the same scale. It's 100-900 instead of Qt's 0-100
328 // See http://www.w3.org/TR/2001/REC-xsl-20011015/slice7.html#font-weight
329 // and http://www.w3.org/TR/CSS2/fonts.html#font-boldness
330 if (font.weight() == QFont::Light) {
331 el->setAttribute(QLatin1String("fo:font-weight"), 200);
332 }
333 else if (font.weight() == QFont::Normal) {
334 // Default
335 //el->setAttribute("fo:font-weight", "normal"); // 400
336 }
337 else if (font.weight() == QFont::DemiBold) {
338 el->setAttribute(QLatin1String("fo:font-weight"), 600);
339 }
340 else if (font.weight() == QFont::Bold) {
341 el->setAttribute(QLatin1String("fo:font-weight"), QLatin1String("bold")); // 700
342 }
343 else if (font.weight() == QFont::Black) {
344 el->setAttribute(QLatin1String("fo:font-weight"), 900);
345 }
346 else {
347 el->setAttribute(QLatin1String("fo:font-weight"), qBound(10, font.weight(), 90) * 10);
348 }
349 // italic, default is "normal"
350 if (font.italic()) {
351 el->setAttribute(QLatin1String("fo:font-style"), QLatin1String("italic"));
352 }
353 // pitch, default is "variable"
354 if (font.fixedPitch()) {
355 el->setAttribute(QLatin1String("style:font-pitch"), QLatin1String("fixed"));
356 }
357 if (!font.family().isEmpty()) {
358 el->setAttribute(QLatin1String("fo:font-family"), font.family());
359 }
360 // kerning, default is "true"
361 KReportUtils::setAttribute(el, QLatin1String("style:letter-kerning"), font.kerning());
362 // underline, default is "none"
363 if (font.underline()) {
364 el->setAttribute(QLatin1String("style:text-underline-type"), QLatin1String("single"));
365 }
366 // stricken-out, default is "none"
367 if (font.strikeOut()) {
368 el->setAttribute(QLatin1String("style:text-line-through-type"), QLatin1String("single"));
369 }
370 el->setAttribute(QLatin1String("fo:font-size"), font.pointSize());
371
372 // letter spacing, default is "normal"
373 // §7.16.2 of [XSL] http://www.w3.org/TR/xsl11/#letter-spacing
375 // A value of 100 will keep the spacing unchanged; a value of 200 will enlarge
376 // the spacing after a character by the width of the character itself.
377 if (font.letterSpacing() != 100.0) {
378 el->setAttribute(QLatin1String("fo:letter-spacing"), roundValueToString(font.letterSpacing()) + QLatin1Char('%'));
379 }
380 }
381 else {
382 // QFont::AbsoluteSpacing
383 // A positive value increases the letter spacing by the corresponding pixels; a negative value decreases the spacing.
384 el->setAttribute(QLatin1String("fo:letter-spacing"), roundValueToString(font.letterSpacing()));
385 }
386}
387
388
389void KReportUtils::buildXMLRect(QDomElement *entity, const QPointF &pos, const QSizeF &size)
390{
391 Q_ASSERT(entity);
392
393 KReportUtils::setAttribute(entity, pos);
394 KReportUtils::setAttribute(entity, size );
395}
396
397void KReportUtils::buildXMLTextStyle(QDomDocument *doc, QDomElement *entity, const KReportTextStyleData &ts)
398{
399 Q_ASSERT(doc);
400 Q_ASSERT(entity);
401 QDomElement element = doc->createElement(QLatin1String("report:text-style"));
402
403 element.setAttribute(QLatin1String("fo:background-color"), ts.backgroundColor.name());
404 element.setAttribute(QLatin1String("fo:foreground-color"), ts.foregroundColor.name());
405 element.setAttribute(QLatin1String("fo:background-opacity"), QString::number(ts.backgroundOpacity) + QLatin1Char('%'));
406 KReportUtils::writeFontAttributes(&element, ts.font);
407
408 entity->appendChild(element);
409}
410
411void KReportUtils::buildXMLLineStyle(QDomDocument *doc, QDomElement *entity, const KReportLineStyle &ls)
412{
413 Q_ASSERT(doc);
414 Q_ASSERT(entity);
415 QDomElement element = doc->createElement(QLatin1String("report:line-style"));
416
417 element.setAttribute(QLatin1String("report:line-color"), ls.color().name());
418 element.setAttribute(QLatin1String("report:line-weight"), ls.weight());
419
420 QString l;
421 switch (ls.penStyle()) {
422 case Qt::NoPen:
423 l = QLatin1String("nopen");
424 break;
425 case Qt::SolidLine:
426 l = QLatin1String("solid");
427 break;
428 case Qt::DashLine:
429 l = QLatin1String("dash");
430 break;
431 case Qt::DotLine:
432 l = QLatin1String("dot");
433 break;
434 case Qt::DashDotLine:
435 l = QLatin1String("dashdot");
436 break;
438 l = QLatin1String("dashdotdot");
439 break;
440 default:
441 l = QLatin1String("solid");
442 }
443 element.setAttribute(QLatin1String("report:line-style"), l);
444
445 entity->appendChild(element);
446}
447
448void KReportUtils::addPropertyAsAttribute(QDomElement* e, KProperty* p)
449{
450 Q_ASSERT(e);
451 Q_ASSERT(p);
452 const QString name = QLatin1String("report:") + QString::fromLatin1(p->name().toLower());
453
454 switch (p->type()) {
455 case QVariant::Int:
456 e->setAttribute(name, p->value().toInt());
457 break;
458 case QVariant::Double:
459 e->setAttribute(name, p->value().toDouble());
460 break;
461 case QVariant::Bool:
462 e->setAttribute(name, p->value().toBool());
463 break;
464 default:
465 e->setAttribute(name, p->value().toString());
466 break;
467 }
468}
469
470bool KReportUtils::setPropertyValue(KProperty *p, const QDomElement &e)
471{
472 const QString name = QStringLiteral("report:") + QString::fromLatin1(p->name());
473 if (!e.hasAttribute(name)) {
474 return false;
475 }
476 QVariant value = e.attribute(name); // string
477 if (!value.convert(p->type())) {
478 return false;
479 }
480 p->setValue(value);
481 return true;
482}
483
484void KReportUtils::setAttribute(QDomElement *e, const QString &attribute, double value)
485{
486 Q_ASSERT(e);
487 QString s;
488 s.setNum(value, 'f', DBL_DIG);
489 e->setAttribute(attribute, s + QLatin1String("pt"));
490}
491
492void KReportUtils::setAttribute(QDomElement *e, const QPointF &value)
493{
494 Q_ASSERT(e);
495 KReportUtils::setAttribute(e, QLatin1String("svg:x"), value.x());
496 KReportUtils::setAttribute(e, QLatin1String("svg:y"), value.y());
497}
498
499void KReportUtils::setAttribute(QDomElement *e, const QSizeF &value)
500{
501 Q_ASSERT(e);
502 KReportUtils::setAttribute(e, QLatin1String("svg:width"), value.width());
503 KReportUtils::setAttribute(e, QLatin1String("svg:height"), value.height());
504}
505
506void KReportUtils::setAttribute(QDomElement *e, const QString &attribute, bool value)
507{
508 e->setAttribute(attribute, value ? QStringLiteral("true") : QStringLiteral("false"));
509}
510
511bool KReportUtils::parseReportTextStyleData(const QDomElement & elemSource, KReportTextStyleData *ts)
512{
513 Q_ASSERT(ts);
514 if (elemSource.tagName() != QLatin1String("report:text-style"))
515 return false;
516 ts->backgroundColor = QColor(elemSource.attribute(
517 QLatin1String("fo:background-color"), QLatin1String("#ffffff")));
518 ts->foregroundColor = QColor(elemSource.attribute(
519 QLatin1String("fo:foreground-color"), QLatin1String("#000000")));
520
521 bool ok;
522 ts->backgroundOpacity = KReportUtils::readPercent(
523 elemSource, QLatin1String("fo:background-opacity"), 100, &ok);
524 if (!ok) {
525 return false;
526 }
527 KReportUtils::readFontAttributes(elemSource, &ts->font);
528 return true;
529}
530
531bool KReportUtils::parseReportLineStyleData(const QDomElement & elemSource, KReportLineStyle *ls)
532{
533 Q_ASSERT(ls);
534 if (elemSource.tagName() == QLatin1String("report:line-style")) {
535 ls->setColor(QColor(elemSource.attribute(QLatin1String("report:line-color"), QLatin1String("#ffffff"))));
536 ls->setWeight(elemSource.attribute(QLatin1String("report:line-weight"), QLatin1String("0.0")).toDouble());
537
538 QString l = elemSource.attribute(QLatin1String("report:line-style"), QLatin1String("nopen"));
539 if (l == QLatin1String("nopen")) {
540 ls->setPenStyle(Qt::NoPen);
541 } else if (l == QLatin1String("solid")) {
542 ls->setPenStyle(Qt::SolidLine);
543 } else if (l == QLatin1String("dash")) {
544 ls->setPenStyle(Qt::DashLine);
545 } else if (l == QLatin1String("dot")) {
546 ls->setPenStyle(Qt::DotLine);
547 } else if (l == QLatin1String("dashdot")) {
548 ls->setPenStyle(Qt::DashDotLine);
549 } else if (l == QLatin1String("dashdotdot")) {
550 ls->setPenStyle(Qt::DashDotDotLine);
551 }
552 return true;
553 }
554 return false;
555}
556
557class PageIds : private QHash<QString, QPageSize::PageSizeId>
558{
559public:
560 PageIds() {}
562 if (isEmpty()) {
563 for (int i = 0; i < QPageSize::LastPageSize; ++i) {
565 if (key.isEmpty()) {
566 break;
567 }
568 insert(key, static_cast<QPageSize::PageSizeId>(i));
569 }
570 }
571 return value(key);
572 }
573};
574
575Q_GLOBAL_STATIC(PageIds, s_pageIds)
576
577QPageSize::PageSizeId KReportUtils::pageSizeId(const QString &key)
578{
579 return s_pageIds->id(key);
580}
581
582QPageSize KReportUtils::pageSize(const QString &key)
583{
584 return QPageSize(s_pageIds->id(key));
585}
int type() const
QVariant value() const
bool setValue(const QVariant &value, ValueOptions options=ValueOptions())
QByteArray name() const
The KReportLineStyle class represents line style.
static qreal parseValue(const QString &value, qreal defaultVal=0.0)
Parses common KReport and ODF values, like "10cm", "5mm" to pt.
QString name(StandardShortcut id)
bool isEmpty() const const
QByteArray toLower() const const
QString name(NameFormat format) const const
QDomElement createElement(const QString &tagName)
QString attribute(const QString &name, const QString &defValue) const const
bool hasAttribute(const QString &name) const const
void setAttribute(const QString &name, const QString &value)
QString tagName() const const
QDomNode appendChild(const QDomNode &newChild)
Capitalization
PercentageSpacing
Capitalization capitalization() const const
QString family() const const
bool fixedPitch() const const
bool italic() const const
bool kerning() const const
qreal letterSpacing() const const
SpacingType letterSpacingType() const const
int pointSize() const const
qreal pointSizeF() const const
void setCapitalization(Capitalization caps)
void setFamily(const QString &family)
void setFixedPitch(bool enable)
void setItalic(bool enable)
void setKerning(bool enable)
void setLetterSpacing(SpacingType type, qreal spacing)
void setPointSizeF(qreal pointSize)
void setStrikeOut(bool enable)
void setUnderline(bool enable)
void setWeight(Weight weight)
bool strikeOut() const const
bool underline() const const
Weight weight() const const
iterator insert(const Key &key, const T &value)
Key key(const T &value) const const
T value(const Key &key) const const
QString key() const const
qreal x() const const
qreal y() const const
void setSize(const QSizeF &size)
void setX(qreal x)
void setY(qreal y)
QSizeF size() const const
qreal x() const const
qreal y() const const
qreal height() const const
void setHeight(qreal height)
void setWidth(qreal width)
qreal width() const const
QString fromLatin1(QByteArrayView str)
bool isEmpty() const const
QString number(double n, char format, int precision)
QString & setNum(double n, char format, int precision)
QByteArray toLatin1() const const
typedef Alignment
PenStyle
bool convert(QMetaType targetType)
bool toBool() const const
double toDouble(bool *ok) const const
int toInt(bool *ok) const const
qreal toReal(bool *ok) const const
QString toString() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:21:31 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.