File indexing completed on 2024-05-12 15:57:04

0001 /* This file is part of the KDE project
0002    SPDX-FileCopyrightText: 2001 David Faure <faure@kde.org>
0003    SPDX-FileCopyrightText: 2004 Nicolas GOUTTE <goutte@kde.org>
0004    SPDX-FileCopyrightText: 2012 Friedrich W. H. Kossebau <kossebau@kde.org>
0005 
0006    SPDX-License-Identifier: LGPL-2.0-or-later
0007 */
0008 
0009 #include "KoUnit.h"
0010 
0011 #include <cmath>
0012 
0013 #include <QTransform>
0014 
0015 #include <klocalizedstring.h>
0016 #include <QtGlobal>
0017 
0018 
0019 // ensure the same order as in KoUnit::Unit
0020 static const char* const unitNameList[KoUnit::TypeCount] =
0021 {
0022     "mm",
0023     "pt",
0024     "in",
0025     "cm",
0026     "dm",
0027     "pi",
0028     "cc",
0029     "px"
0030 };
0031 
0032 QString KoUnit::unitDescription(KoUnit::Type type)
0033 {
0034     switch (type) {
0035     case KoUnit::Millimeter:
0036         return i18n("Millimeters (mm)");
0037     case KoUnit::Centimeter:
0038         return i18n("Centimeters (cm)");
0039     case KoUnit::Decimeter:
0040         return i18n("Decimeters (dm)");
0041     case KoUnit::Inch:
0042         return i18n("Inches (in)");
0043     case KoUnit::Pica:
0044         return i18n("Pica (pi)");
0045     case KoUnit::Cicero:
0046         return i18n("Cicero (cc)");
0047     case KoUnit::Point:
0048         return i18n("Points (pt)");
0049     case KoUnit::Pixel:
0050         return i18n("Pixels (px)");
0051     default:
0052         return i18n("Unsupported unit");
0053     }
0054 }
0055 
0056 // grouped by units which are similar
0057 static const KoUnit::Type typesInUi[KoUnit::TypeCount] =
0058 {
0059     KoUnit::Millimeter,
0060     KoUnit::Centimeter,
0061     KoUnit::Decimeter,
0062     KoUnit::Inch,
0063     KoUnit::Pica,
0064     KoUnit::Cicero,
0065     KoUnit::Point,
0066     KoUnit::Pixel,
0067 };
0068 
0069 QStringList KoUnit::listOfUnitNameForUi(ListOptions listOptions)
0070 {
0071     QStringList lst;
0072     for (int i = 0; i < KoUnit::TypeCount; ++i) {
0073         const Type type = typesInUi[i];
0074         if ((type != Pixel) || ((listOptions & HideMask) == ListAll))
0075             lst.append(unitDescription(type));
0076     }
0077     return lst;
0078 }
0079 
0080 KoUnit KoUnit::fromListForUi(int index, ListOptions listOptions, qreal factor)
0081 {
0082     KoUnit::Type type = KoUnit::Point;
0083 
0084     if ((0 <= index) && (index < KoUnit::TypeCount)) {
0085         // iterate through all enums and skip the Pixel enum if needed
0086         for (int i = 0; i < KoUnit::TypeCount; ++i) {
0087             if ((listOptions&HidePixel) && (typesInUi[i] == Pixel)) {
0088                 ++index;
0089                 continue;
0090             }
0091             if (i == index) {
0092                 type = typesInUi[i];
0093                 break;
0094             }
0095         }
0096     }
0097 
0098     return KoUnit(type, factor);
0099 }
0100 
0101 int KoUnit::indexInListForUi(ListOptions listOptions) const
0102 {
0103     if ((listOptions&HidePixel) && (m_type == Pixel)) {
0104         return -1;
0105     }
0106 
0107     int result = -1;
0108 
0109     int skipped = 0;
0110     for (int i = 0; i < KoUnit::TypeCount; ++i) {
0111         if ((listOptions&HidePixel) && (typesInUi[i] == Pixel)) {
0112             ++skipped;
0113             continue;
0114         }
0115         if (typesInUi[i] == m_type) {
0116             result = i - skipped;
0117             break;
0118         }
0119     }
0120 
0121     return result;
0122 }
0123 
0124 
0125 
0126 qreal KoUnit::toUserValueRounded(const qreal value) const
0127 {
0128     qreal userValue = toUserValuePrecise(value);
0129     qreal rounding = 1.0;
0130 
0131     switch (m_type) {
0132     case Pixel:
0133         return userValue; // no rounding for Pixel value
0134     case Millimeter:
0135         rounding = MM_ROUNDING;
0136         break;
0137     case Centimeter:
0138         rounding = CM_ROUNDING;
0139         break;
0140     case Decimeter:
0141         rounding = DM_ROUNDING;
0142         break;
0143     case Inch:
0144         rounding = IN_ROUNDING;
0145         break;
0146     case Pica:
0147         rounding = PI_ROUNDING;
0148         break;
0149     case Cicero:
0150         rounding = CC_ROUNDING;
0151         break;
0152     case Point:
0153     default:
0154         rounding = PT_ROUNDING;
0155     }
0156 
0157 
0158     return floor(userValue * rounding) / rounding;
0159 }
0160 
0161 qreal KoUnit::toUserValuePrecise(const qreal ptValue) const
0162 {
0163     switch (m_type) {
0164     case Millimeter:
0165         return POINT_TO_MM(ptValue);
0166     case Centimeter:
0167         return POINT_TO_CM(ptValue);
0168     case Decimeter:
0169         return POINT_TO_DM(ptValue);
0170     case Inch:
0171         return POINT_TO_INCH(ptValue);
0172     case Pica:
0173         return POINT_TO_PI(ptValue);
0174     case Cicero:
0175         return POINT_TO_CC(ptValue);
0176     case Pixel:
0177         return ptValue * m_pixelConversion;
0178     case Point:
0179     default:
0180         return ptValue;
0181     }
0182 }
0183 
0184 
0185 
0186 qreal KoUnit::toUserValue(qreal ptValue, bool rounding) const
0187 {
0188     if (rounding) {
0189         return toUserValueRounded(ptValue);
0190     }
0191     else {
0192         return toUserValuePrecise(ptValue);
0193     }
0194 }
0195 
0196 QString KoUnit::toUserStringValue(qreal ptValue) const
0197 {
0198     return QLocale().toString(toUserValue(ptValue));
0199 }
0200 
0201 qreal KoUnit::fromUserValue(qreal value) const
0202 {
0203     switch (m_type) {
0204     case Millimeter:
0205         return MM_TO_POINT(value);
0206     case Centimeter:
0207         return CM_TO_POINT(value);
0208     case Decimeter:
0209         return DM_TO_POINT(value);
0210     case Inch:
0211         return INCH_TO_POINT(value);
0212     case Pica:
0213         return PI_TO_POINT(value);
0214     case Cicero:
0215         return CC_TO_POINT(value);
0216     case Pixel:
0217         return value / m_pixelConversion;
0218     case Point:
0219     default:
0220         return value;
0221     }
0222 }
0223 
0224 qreal KoUnit::fromUserValue(const QString &value, bool *ok) const
0225 {
0226     return fromUserValue(QLocale().toDouble(value, ok));
0227 }
0228 
0229 qreal KoUnit::parseValue(const QString& _value, qreal defaultVal)
0230 {
0231     if (_value.isEmpty())
0232         return defaultVal;
0233 
0234     QString value(_value.simplified());
0235     value.remove(QLatin1Char(' '));
0236 
0237     int firstLetter = -1;
0238     for (int i = 0; i < value.length(); ++i) {
0239         if (value.at(i).isLetter()) {
0240             if (value.at(i) == QLatin1Char('e'))
0241                 continue;
0242             firstLetter = i;
0243             break;
0244         }
0245     }
0246 
0247     if (firstLetter == -1)
0248         return value.toDouble();
0249 
0250     const QString symbol = value.mid(firstLetter);
0251     value.truncate(firstLetter);
0252     const qreal val = value.toDouble();
0253 
0254     if (symbol == QLatin1String("pt"))
0255         return val;
0256 
0257     bool ok;
0258     KoUnit u = KoUnit::fromSymbol(symbol, &ok);
0259     if (ok)
0260         return u.fromUserValue(val);
0261 
0262     if (symbol == QLatin1String("m"))
0263         return DM_TO_POINT(val * 10.0);
0264     else if (symbol == QLatin1String("km"))
0265         return DM_TO_POINT(val * 10000.0);
0266 
0267     // TODO : add support for mi/ft ?
0268     return defaultVal;
0269 }
0270 
0271 KoUnit KoUnit::fromSymbol(const QString &symbol, bool *ok)
0272 {
0273     Type result = Point;
0274 
0275     if (symbol == QLatin1String("inch") /*compat*/) {
0276         result = Inch;
0277         if (ok)
0278             *ok = true;
0279     } else {
0280         if (ok)
0281             *ok = false;
0282 
0283         for (int i = 0; i < TypeCount; ++i) {
0284             if (symbol == QLatin1String(unitNameList[i])) {
0285                 result = static_cast<Type>(i);
0286                 if (ok)
0287                     *ok = true;
0288             }
0289         }
0290     }
0291 
0292     return KoUnit(result);
0293 }
0294 
0295 qreal KoUnit::convertFromUnitToUnit(const qreal value, const KoUnit &fromUnit, const KoUnit &toUnit, qreal factor)
0296 {
0297     qreal pt;
0298     switch (fromUnit.type()) {
0299     case Millimeter:
0300         pt = MM_TO_POINT(value);
0301         break;
0302     case Centimeter:
0303         pt = CM_TO_POINT(value);
0304         break;
0305     case Decimeter:
0306         pt = DM_TO_POINT(value);
0307         break;
0308     case Inch:
0309         pt = INCH_TO_POINT(value);
0310         break;
0311     case Pica:
0312         pt = PI_TO_POINT(value);
0313         break;
0314     case Cicero:
0315         pt = CC_TO_POINT(value);
0316         break;
0317     case Pixel:
0318         pt = value / factor;
0319         break;
0320     case Point:
0321     default:
0322         pt = value;
0323     }
0324 
0325     switch (toUnit.type()) {
0326     case Millimeter:
0327         return POINT_TO_MM(pt);
0328     case Centimeter:
0329         return POINT_TO_CM(pt);
0330     case Decimeter:
0331         return POINT_TO_DM(pt);
0332     case Inch:
0333         return POINT_TO_INCH(pt);
0334     case Pica:
0335         return POINT_TO_PI(pt);
0336     case Cicero:
0337         return POINT_TO_CC(pt);
0338     case Pixel:
0339         return pt * factor;
0340     case Point:
0341     default:
0342         return pt;
0343     }
0344 
0345 }
0346 
0347 QString KoUnit::symbol() const
0348 {
0349     return QLatin1String(unitNameList[m_type]);
0350 }
0351 
0352 qreal KoUnit::parseAngle(const QString& _value, qreal defaultVal)
0353 {
0354     if (_value.isEmpty())
0355         return defaultVal;
0356 
0357     QString value(_value.simplified());
0358     value.remove(QLatin1Char(' '));
0359 
0360     int firstLetter = -1;
0361     for (int i = 0; i < value.length(); ++i) {
0362         if (value.at(i).isLetter()) {
0363             if (value.at(i) == QLatin1Char('e'))
0364                 continue;
0365             firstLetter = i;
0366             break;
0367         }
0368     }
0369 
0370     if (firstLetter == -1)
0371         return value.toDouble();
0372 
0373     const QString type = value.mid(firstLetter);
0374     value.truncate(firstLetter);
0375     const qreal val = value.toDouble();
0376 
0377     if (type == QLatin1String("deg"))
0378         return val;
0379     else if (type == QLatin1String("rad"))
0380         return val * 180 / M_PI;
0381     else if (type == QLatin1String("grad"))
0382         return val * 0.9;
0383 
0384     return defaultVal;
0385 }
0386 
0387 qreal KoUnit::approxTransformScale(const QTransform &t)
0388 {
0389     return std::sqrt(qAbs(t.determinant()));
0390 }
0391 
0392 void KoUnit::adjustByPixelTransform(const QTransform &t)
0393 {
0394     m_pixelConversion *= approxTransformScale(t);
0395 }
0396 
0397 #ifndef QT_NO_DEBUG_STREAM
0398 QDebug operator<<(QDebug debug, const KoUnit &unit)
0399 {
0400 #ifndef NDEBUG
0401     debug.nospace() << unit.symbol();
0402 #else
0403     Q_UNUSED(unit);
0404 #endif
0405     return debug.space();
0406 
0407 }
0408 #endif