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