File indexing completed on 2024-04-21 04:41:51

0001 /* This file is part of the KDE project
0002    Copyright (C) 2001 David Faure <faure@kde.org>
0003    Copyright (C) 2004 Nicolas GOUTTE <goutte@kde.org>
0004    Copyright (C) 2012 Friedrich W. H. Kossebau <kossebau@kde.org>
0005    Copyright (C) 2017 Jarosław Staniek <staniek@kde.org>
0006 
0007    This library is free software; you can redistribute it and/or
0008    modify it under the terms of the GNU Library General Public
0009    License as published by the Free Software Foundation; either
0010    version 2 of the License, or (at your option) any later version.
0011 
0012    This library is distributed in the hope that it will be useful,
0013    but WITHOUT ANY WARRANTY; without even the implied warranty of
0014    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0015    Library General Public License for more details.
0016 
0017    You should have received a copy of the GNU Library General Public License
0018    along with this library; see the file COPYING.LIB.  If not, write to
0019    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0020  * Boston, MA 02110-1301, USA.
0021 */
0022 
0023 #include "KReportUnit.h"
0024 #include "kreport_debug.h"
0025 
0026 #include <cmath>
0027 
0028 #include <QTransform>
0029 #include <QCoreApplication>
0030 
0031 class Q_DECL_HIDDEN KReportUnit::Private
0032 {
0033 public:
0034     KReportUnit::Type type;
0035     qreal pixelConversion;
0036 };
0037 
0038 // unit types
0039 // Note: ensure the same order as in KReportUnit::Unit
0040 static QList<KReportUnit::Type> s_unitTypes = {
0041     KReportUnit::Type::Millimeter,
0042     KReportUnit::Type::Centimeter,
0043     KReportUnit::Type::Decimeter,
0044     KReportUnit::Type::Inch,
0045     KReportUnit::Type::Pica,
0046     KReportUnit::Type::Cicero,
0047     KReportUnit::Type::Point,
0048     KReportUnit::Type::Pixel
0049 };
0050 static int firstUnitIndex()
0051 {
0052     return static_cast<int>(KReportUnit::Type::Invalid) + 1;
0053 }
0054 
0055 static int lastUnitIndex()
0056 {
0057     return static_cast<int>(KReportUnit::Type::Last); // without Invalid
0058 }
0059 
0060 // unit symbols
0061 // Note: ensure the same order as in KReportUnit::Unit
0062 static const char* const s_unitSymbols[] =
0063 {
0064     nullptr,
0065     "mm",
0066     "cm",
0067     "dm",
0068     "in",
0069     "pi",
0070     "cc",
0071     "pt",
0072     "px"
0073 };
0074 
0075 KReportUnit::KReportUnit() : d(new Private)
0076 {
0077     d->type = Type::Invalid;
0078     d->pixelConversion = 1.0;
0079 }
0080 
0081 KReportUnit::KReportUnit(Type type, qreal factor) : d(new Private)
0082 {
0083     d->type = type;
0084     d->pixelConversion = factor;
0085 }
0086 
0087 KReportUnit::KReportUnit(const KReportUnit& other) : d(new Private)
0088 {
0089     d->type = other.type();
0090     d->pixelConversion = other.factor();
0091 }
0092 
0093 KReportUnit::~KReportUnit()
0094 {
0095     delete d;
0096 }
0097 
0098 bool KReportUnit::operator==(const KReportUnit& other) const 
0099 {
0100     return d->type == other.d->type &&
0101            (d->type != Type::Pixel ||
0102             qFuzzyCompare(d->pixelConversion, other.d->pixelConversion));
0103 }
0104 
0105 bool KReportUnit::operator!=(const KReportUnit& other) const
0106 {
0107     return !operator==(other);
0108 }
0109 
0110 
0111 KReportUnit& KReportUnit::operator=(Type type)
0112 {
0113     d->type = type;
0114     d->pixelConversion = 1.0;
0115     return *this;
0116 }
0117 
0118 KReportUnit & KReportUnit::operator=(const KReportUnit& other)
0119 {
0120     d->type = other.type();
0121     d->pixelConversion = other.factor();
0122     return *this;
0123 }
0124 
0125 //static
0126 QList<KReportUnit::Type> KReportUnit::allTypes()
0127 {
0128     return s_unitTypes;
0129 }
0130 
0131 //static
0132 QString KReportUnit::description(KReportUnit::Type type)
0133 {
0134     switch (type) {
0135     case KReportUnit::Type::Invalid:
0136         return tr("Invalid");
0137     case KReportUnit::Type::Millimeter:
0138         return tr("Millimeters (mm)");
0139     case KReportUnit::Type::Centimeter:
0140         return tr("Centimeters (cm)");
0141     case KReportUnit::Type::Decimeter:
0142         return tr("Decimeters (dm)");
0143     case KReportUnit::Type::Inch:
0144         return tr("Inches (in)");
0145     case KReportUnit::Type::Pica:
0146         return tr("Pica (pi)");
0147     case KReportUnit::Type::Cicero:
0148         return tr("Cicero (cc)");
0149     case KReportUnit::Type::Point:
0150         return tr("Points (pt)");
0151     case KReportUnit::Type::Pixel:
0152         return tr("Pixels (px)");
0153     default:
0154         return tr("Unsupported unit");
0155     }
0156 }
0157 
0158 QString KReportUnit::description() const
0159 {
0160     return KReportUnit::description(type());
0161 }
0162 
0163 QStringList KReportUnit::descriptions(const QList<Type> &types)
0164 {
0165     QStringList result;
0166     for (Type t : types) {
0167         result.append(description(t));
0168     }
0169     return result;
0170 }
0171 
0172 qreal KReportUnit::toUserValue(qreal ptValue) const
0173 {
0174     switch (d->type) {
0175     case Type::Invalid:
0176         kreportWarning() << "Conversion for Invalid type not supported";
0177         return -1.0;
0178     case Type::Millimeter:
0179         return toMillimeter(ptValue);
0180     case Type::Centimeter:
0181         return toCentimeter(ptValue);
0182     case Type::Decimeter:
0183         return toDecimeter(ptValue);
0184     case Type::Inch:
0185         return toInch(ptValue);
0186     case Type::Pica:
0187         return toPica(ptValue);
0188     case Type::Cicero:
0189         return toCicero(ptValue);
0190     case Type::Pixel:
0191         return ptValue * d->pixelConversion;
0192     case Type::Point:
0193     default:
0194         return toPoint(ptValue);
0195     }
0196 }
0197 
0198 qreal KReportUnit::ptToUnit(qreal ptValue, const KReportUnit &unit)
0199 {
0200     switch (unit.d->type) {
0201     case Type::Invalid:
0202         return -1.0;
0203     case Type::Millimeter:
0204         return POINT_TO_MM(ptValue);
0205     case Type::Centimeter:
0206         return POINT_TO_CM(ptValue);
0207     case Type::Decimeter:
0208         return POINT_TO_DM(ptValue);
0209     case Type::Inch:
0210         return POINT_TO_INCH(ptValue);
0211     case Type::Pica:
0212         return POINT_TO_PI(ptValue);
0213     case Type::Cicero:
0214         return POINT_TO_CC(ptValue);
0215     case Type::Pixel:
0216         return ptValue * unit.d->pixelConversion;
0217     case Type::Point:
0218     default:
0219         return ptValue;
0220     }
0221 }
0222 
0223 QString KReportUnit::toUserStringValue(qreal ptValue) const
0224 {
0225     return QLocale::system().toString(toUserValue(ptValue));
0226 }
0227 
0228 qreal KReportUnit::convertFromPoint(qreal ptValue) const
0229 {
0230     switch (d->type) {
0231     case Type::Millimeter:
0232         return POINT_TO_MM(ptValue);
0233     case Type::Centimeter:
0234         return POINT_TO_CM(ptValue);
0235     case Type::Decimeter:
0236         return POINT_TO_DM(ptValue);
0237     case Type::Inch:
0238         return POINT_TO_INCH(ptValue);
0239     case Type::Pica:
0240         return POINT_TO_PI(ptValue);
0241     case Type::Cicero:
0242         return POINT_TO_CC(ptValue);
0243     case Type::Pixel:
0244         return ptValue * d->pixelConversion;
0245     case Type::Point:
0246     default:
0247         return ptValue;
0248     }
0249 }
0250 
0251 QPointF KReportUnit::convertFromPoint(const QPointF &ptValue) const
0252 {
0253     return QPointF(convertFromPoint(ptValue.x()), convertFromPoint(ptValue.y()));
0254 }
0255 
0256 QSizeF KReportUnit::convertFromPoint(const QSizeF &ptValue) const
0257 {
0258     return QSizeF(convertFromPoint(ptValue.width()), convertFromPoint(ptValue.height()));
0259 }
0260 
0261 qreal KReportUnit::convertToPoint(qreal value) const
0262 {
0263     switch (d->type) {
0264     case Type::Invalid:
0265         return -1.0;
0266     case Type::Millimeter:
0267         return MM_TO_POINT(value);
0268     case Type::Centimeter:
0269         return CM_TO_POINT(value);
0270     case Type::Decimeter:
0271         return DM_TO_POINT(value);
0272     case Type::Inch:
0273         return INCH_TO_POINT(value);
0274     case Type::Pica:
0275         return PI_TO_POINT(value);
0276     case Type::Cicero:
0277         return CC_TO_POINT(value);
0278     case Type::Pixel:
0279         return value / d->pixelConversion;
0280     case Type::Point:
0281     default:
0282         return value;
0283     }
0284 }
0285 
0286 qreal KReportUnit::convertToPoint(const QString &value, bool *ok) const
0287 {
0288     return convertToPoint(QLocale::system().toDouble(value, ok));
0289 }
0290 
0291 QPointF KReportUnit::convertToPoint(const QPointF &value) const
0292 {
0293     return QPointF(KReportUnit::convertToPoint(value.x()),
0294                    KReportUnit::convertToPoint(value.y()));
0295 }
0296 
0297 QSizeF KReportUnit::convertToPoint(const QSizeF &value) const
0298 {
0299     return QSizeF(KReportUnit::convertToPoint(value.width()),
0300                   KReportUnit::convertToPoint(value.height()));
0301 }
0302 
0303 qreal KReportUnit::parseValue(const QString& _value, qreal defaultVal)
0304 {
0305     if (_value.isEmpty())
0306         return defaultVal;
0307 
0308     QString value(_value.simplified());
0309     value.remove(QLatin1Char(' '));
0310 
0311     int firstLetter = -1;
0312     for (int i = 0; i < value.length(); ++i) {
0313         if (value.at(i).isLetter()) {
0314             if (value.at(i) == QLatin1Char('e'))
0315                 continue;
0316             firstLetter = i;
0317             break;
0318         }
0319     }
0320 
0321     bool ok;
0322     if (firstLetter == -1) {
0323         qreal result = QVariant(value).toReal(&ok);
0324         return ok ? result : defaultVal;
0325     }
0326 
0327     const QByteArray symbol = value.mid(firstLetter).toLatin1();
0328     value.truncate(firstLetter);
0329     const qreal val = value.toDouble();
0330 
0331     if (symbol == "pt" || symbol.isEmpty())
0332         return val;
0333 
0334     KReportUnit u(KReportUnit::symbolToType(QLatin1String(symbol)));
0335     if (u.isValid()) {
0336         return u.fromUserValue(val);
0337     }
0338 
0339     if (symbol == "m")
0340         return DM_TO_POINT(val * 10.0);
0341     else if (symbol == "km")
0342         return DM_TO_POINT(val * 10000.0);
0343     kreportWarning() << "KReportUnit::parseValue: Unit" << symbol << "is not supported, please report.";
0344 
0345     //! @todo add support for mi/ft ?
0346     return defaultVal;
0347 }
0348 
0349 //static
0350 QString KReportUnit::symbol(KReportUnit::Type type)
0351 {
0352     return QLatin1String(s_unitSymbols[static_cast<int>(type)]);
0353 }
0354 
0355 //static
0356 KReportUnit::Type KReportUnit::symbolToType(const QString &symbol)
0357 {
0358     Type result = Type::Invalid;
0359 
0360     if (symbol == QLatin1String("inch") /*compat*/) {
0361         result = Type::Inch;
0362     } else {
0363         for (int i = firstUnitIndex(); i <= lastUnitIndex(); ++i) {
0364             if (symbol == QLatin1String(s_unitSymbols[i])) {
0365                 result = static_cast<Type>(i);
0366                 break;
0367             }
0368         }
0369     }
0370     return result;
0371 }
0372 
0373 //static
0374 QStringList KReportUnit::symbols(const QList<Type> &types)
0375 {
0376     QStringList result;
0377     for (Type t : types) {
0378         result.append(symbol(t));
0379     }
0380     return result;
0381 }
0382 
0383 qreal KReportUnit::convertFromUnitToUnit(qreal value, const KReportUnit &fromUnit, const KReportUnit &toUnit, qreal factor)
0384 {
0385     qreal pt;
0386     switch (fromUnit.type()) {
0387     case Type::Invalid:
0388         pt = -1.0;
0389         break;
0390     case Type::Millimeter:
0391         pt = MM_TO_POINT(value);
0392         break;
0393     case Type::Centimeter:
0394          pt = CM_TO_POINT(value);
0395          break;
0396     case Type::Decimeter:
0397          pt = DM_TO_POINT(value);
0398          break;
0399     case Type::Inch:
0400          pt = INCH_TO_POINT(value);
0401          break;
0402     case Type::Pica:
0403          pt = PI_TO_POINT(value);
0404          break;
0405     case Type::Cicero:
0406          pt = CC_TO_POINT(value);
0407          break;
0408     case Type::Pixel:
0409          pt = value / factor;
0410          break;
0411     case Type::Point:
0412     default:
0413          pt = value;
0414      }
0415  
0416     switch (toUnit.type()) {
0417     case Type::Millimeter:
0418          return POINT_TO_MM(pt);
0419     case Type::Centimeter:
0420          return POINT_TO_CM(pt);
0421     case Type::Decimeter:
0422          return POINT_TO_DM(pt);
0423     case Type::Inch:
0424          return POINT_TO_INCH(pt);
0425     case Type::Pica:
0426          return POINT_TO_PI(pt);
0427     case Type::Cicero:
0428          return POINT_TO_CC(pt);
0429     case Type::Pixel:
0430          return pt * factor;
0431     case Type::Invalid:
0432     default:
0433          return pt;
0434      }
0435 }
0436  
0437 QPointF KReportUnit::convertFromUnitToUnit(const QPointF &value,
0438                                            const KReportUnit &fromUnit,
0439                                            const KReportUnit &toUnit)
0440 {
0441     return QPointF(
0442         KReportUnit::convertFromUnitToUnit(value.x(), fromUnit, toUnit),
0443         KReportUnit::convertFromUnitToUnit(value.y(), fromUnit, toUnit));
0444 }
0445 
0446 QSizeF KReportUnit::convertFromUnitToUnit(const QSizeF &value,
0447                                            const KReportUnit &fromUnit,
0448                                            const KReportUnit &toUnit)
0449 {
0450     return QSizeF(
0451         KReportUnit::convertFromUnitToUnit(value.width(), fromUnit, toUnit),
0452         KReportUnit::convertFromUnitToUnit(value.height(), fromUnit, toUnit));
0453 }
0454 
0455 QString KReportUnit::symbol() const
0456 {
0457     return QLatin1String(s_unitSymbols[static_cast<int>(d->type)]);
0458 }
0459 
0460 qreal KReportUnit::parseAngle(const QString& _value, qreal defaultVal)
0461 {
0462     if (_value.isEmpty())
0463         return defaultVal;
0464 
0465     QString value(_value.simplified());
0466     value.remove(QLatin1Char(' '));
0467 
0468     int firstLetter = -1;
0469     for (int i = 0; i < value.length(); ++i) {
0470         if (value.at(i).isLetter()) {
0471             if (value.at(i) == QLatin1Char('e'))
0472                 continue;
0473             firstLetter = i;
0474             break;
0475         }
0476     }
0477 
0478     if (firstLetter == -1)
0479         return value.toDouble();
0480 
0481     const QString type = value.mid(firstLetter);
0482     value.truncate(firstLetter);
0483     const qreal val = value.toDouble();
0484 
0485     if (type == QLatin1String("deg"))
0486         return val;
0487     else if (type == QLatin1String("rad"))
0488         return val * 180 / M_PI;
0489     else if (type == QLatin1String("grad"))
0490         return val * 0.9;
0491 
0492     return defaultVal;
0493 }
0494 
0495 #ifndef QT_NO_DEBUG_STREAM
0496 QDebug operator<<(QDebug debug, const KReportUnit &unit)
0497 {
0498 #ifndef NDEBUG
0499     if (unit.isValid()) {
0500         debug.nospace() << QString::fromLatin1("Unit(%1, %2)").arg(unit.symbol()).arg(unit.factor());
0501     } else {
0502         debug.nospace() << QString::fromLatin1("Unit(Invalid)");
0503     }
0504 #else
0505     Q_UNUSED(unit);
0506 #endif
0507     return debug.space();
0508 }
0509 
0510 void KReportUnit::setFactor(qreal factor) 
0511 {
0512     d->pixelConversion = factor;
0513 }
0514 
0515 qreal KReportUnit::factor() const
0516 {
0517     return d->pixelConversion;
0518 }
0519 
0520 KReportUnit::Type KReportUnit::type() const 
0521 {
0522     return d->type;
0523 }
0524 
0525 bool KReportUnit::isValid() const
0526 {
0527     return d->type != KReportUnit::Type::Invalid;
0528 }
0529 
0530 #endif