File indexing completed on 2024-05-05 16:09:02

0001 /*
0002     SPDX-FileCopyrightText: 2018 Alexander Stippich <a.stippich@gmx.net>
0003 
0004     SPDX-License-Identifier: LGPL-2.1-or-later
0005 */
0006 
0007 #include "formatstrings_p.h"
0008 
0009 #include <math.h>
0010 #include <QDateTime>
0011 #include <KLocalizedString>
0012 #include <KFormat>
0013 
0014 using namespace KFileMetaData;
0015 
0016 /*
0017  * Calculates and returns the number of digits after the
0018  * comma to always show three significant digits for use
0019  * with KFormat::formatValue.
0020  */
0021 int threeSignificantDigits(int value)
0022 {
0023     if (value == 0) {
0024         return 0;
0025     }
0026     int before_decimal_point = static_cast<int>(log10(value > 0 ? value : -value)) % 3;
0027     return 2 - before_decimal_point;
0028 }
0029 
0030 QString FormatStrings::toStringFunction(const QVariant& value)
0031 {
0032     return value.toString();
0033 }
0034 
0035 QString FormatStrings::formatDouble(const QVariant& value)
0036 {
0037     return QLocale().toString(value.toDouble(),'g',3);
0038 }
0039 
0040 QString FormatStrings::formatDate(const QVariant& value)
0041 {
0042     KFormat form;
0043     QDateTime dt;
0044     if (value.type() == QVariant::DateTime) {
0045         dt = value.toDateTime();
0046     } else {
0047         dt = QDateTime::fromString(value.toString(), Qt::ISODate);
0048     }
0049     if (dt.isValid()) {
0050         return form.formatRelativeDateTime(dt, QLocale::LongFormat);
0051     }
0052     return QString();
0053 }
0054 
0055 QString FormatStrings::formatDuration(const QVariant& value)
0056 {
0057     KFormat form;
0058     return form.formatDuration(value.toInt() * 1000);
0059 }
0060 
0061 QString FormatStrings::formatBitRate(const QVariant& value)
0062 {
0063     KFormat form;
0064     return i18nc("@label bitrate (per second)", "%1/s", form.formatValue(value.toInt(),
0065          KFormat::Unit::Bit, threeSignificantDigits(value.toInt()), KFormat::UnitPrefix::AutoAdjust, KFormat::MetricBinaryDialect));
0066 }
0067 
0068 QString FormatStrings::formatSampleRate(const QVariant& value)
0069 {
0070     KFormat form;
0071     return form.formatValue(value.toInt(), KFormat::Unit::Hertz, threeSignificantDigits(value.toInt()), KFormat::UnitPrefix::AutoAdjust, KFormat::MetricBinaryDialect);
0072 }
0073 
0074 QString FormatStrings::formatOrientationValue(const QVariant& value)
0075 {
0076     QString string;
0077     switch (value.toInt()) {
0078     case 1: string = i18nc("Description of image orientation", "Unchanged"); break;
0079     case 2: string = i18nc("Description of image orientation", "Horizontally flipped"); break;
0080     case 3: string = i18nc("Description of image orientation", "180° rotated"); break;
0081     case 4: string = i18nc("Description of image orientation", "Vertically flipped"); break;
0082     case 5: string = i18nc("Description of image orientation", "Transposed"); break;
0083     case 6: string = i18nc("Description of image orientation, counter clock-wise rotated", "90° rotated CCW "); break;
0084     case 7: string = i18nc("Description of image orientation", "Transversed"); break;
0085     case 8: string = i18nc("Description of image orientation, counter clock-wise rotated", "270° rotated CCW"); break;
0086     default:
0087         break;
0088     }
0089     return string;
0090 }
0091 
0092 QString FormatStrings::formatPhotoFlashValue(const QVariant& value)
0093 {
0094     // copied from exiv2 tags_int.cpp
0095     const QMap<int, QString> flashTranslation = {
0096         { 0x00, i18nc("Description of photo flash", "No flash")                                                      },
0097         { 0x01, i18nc("Description of photo flash", "Fired")                                                         },
0098         { 0x05, i18nc("Description of photo flash", "Fired, return light not detected")                              },
0099         { 0x07, i18nc("Description of photo flash", "Fired, return light detected")                                  },
0100         { 0x08, i18nc("Description of photo flash", "Yes, did not fire")                                             },
0101         { 0x09, i18nc("Description of photo flash", "Yes, compulsory")                                               },
0102         { 0x0d, i18nc("Description of photo flash", "Yes, compulsory, return light not detected")                    },
0103         { 0x0f, i18nc("Description of photo flash", "Yes, compulsory, return light detected")                        },
0104         { 0x10, i18nc("Description of photo flash", "No, compulsory")                                                },
0105         { 0x14, i18nc("Description of photo flash", "No, did not fire, return light not detected")                   },
0106         { 0x18, i18nc("Description of photo flash", "No, auto")                                                      },
0107         { 0x19, i18nc("Description of photo flash", "Yes, auto")                                                     },
0108         { 0x1d, i18nc("Description of photo flash", "Yes, auto, return light not detected")                          },
0109         { 0x1f, i18nc("Description of photo flash", "Yes, auto, return light detected")                              },
0110         { 0x20, i18nc("Description of photo flash", "No flash function")                                             },
0111         { 0x30, i18nc("Description of photo flash", "No, no flash function")                                         },
0112         { 0x41, i18nc("Description of photo flash", "Yes, red-eye reduction")                                        },
0113         { 0x45, i18nc("Description of photo flash", "Yes, red-eye reduction, return light not detected")             },
0114         { 0x47, i18nc("Description of photo flash", "Yes, red-eye reduction, return light detected")                 },
0115         { 0x49, i18nc("Description of photo flash", "Yes, compulsory, red-eye reduction")                            },
0116         { 0x4d, i18nc("Description of photo flash", "Yes, compulsory, red-eye reduction, return light not detected") },
0117         { 0x4f, i18nc("Description of photo flash", "Yes, compulsory, red-eye reduction, return light detected")     },
0118         { 0x50, i18nc("Description of photo flash", "No, red-eye reduction")                                         },
0119         { 0x58, i18nc("Description of photo flash", "No, auto, red-eye reduction")                                   },
0120         { 0x59, i18nc("Description of photo flash", "Yes, auto, red-eye reduction")                                  },
0121         { 0x5d, i18nc("Description of photo flash", "Yes, auto, red-eye reduction, return light not detected")       },
0122         { 0x5f, i18nc("Description of photo flash", "Yes, auto, red-eye reduction, return light detected")           }
0123     };
0124     if (flashTranslation.contains(value.toInt())) {
0125         return flashTranslation.value(value.toInt());
0126     } else {
0127         return i18n("Unknown");
0128     }
0129 
0130 }
0131 
0132 QString FormatStrings::formatAsDegree(const QVariant& value)
0133 {
0134     return i18nc("Symbol of degree, no space", "%1°", QLocale().toString(value.toDouble()));
0135 }
0136 
0137 QString FormatStrings::formatAsMeter(const QVariant& value)
0138 {
0139     KFormat form;
0140     return form.formatValue(value.toDouble(), KFormat::Unit::Meter, 1, KFormat::UnitPrefix::AutoAdjust, KFormat::MetricBinaryDialect);
0141 }
0142 
0143 QString FormatStrings::formatAsMilliMeter(const QVariant& value)
0144 {
0145     return i18nc("Focal length given in mm", "%1 mm", QLocale().toString(value.toDouble(), 'g', 3));
0146 }
0147 
0148 QString FormatStrings::formatAsFrameRate(const QVariant& value)
0149 {
0150     return i18nc("Symbol of frames per second, with space", "%1 fps", QLocale().toString(round(value.toDouble() * 100) / 100));
0151 }
0152 
0153 QString FormatStrings::formatPhotoTime(const QVariant& value)
0154 {
0155     auto val = value.toDouble();
0156     if (val < 0.3 && !qFuzzyIsNull(val)) {
0157         auto reciprocal = 1.0/val;
0158         auto roundedReciprocal = round(reciprocal);
0159         if (abs(reciprocal - roundedReciprocal) < 1e-3) {
0160             return i18nc("Time period given in seconds as rational number, denominator is given", "1/%1 s", roundedReciprocal);
0161         }
0162     }
0163     return i18nc("Time period given in seconds", "%1 s", QLocale().toString(value.toDouble(), 'g', 3));
0164 }
0165 
0166 QString FormatStrings::formatPhotoExposureBias(const QVariant& value)
0167 {
0168     QLocale locale;
0169     auto val = value.toDouble();
0170     /*
0171      * Exposure values are mostly in steps of one half or third.
0172      * Try to construct a rational number from it.
0173      * Output as double when it is not possible.
0174      */
0175     auto sixthParts = val * 6;
0176     int roundedSixthParts = static_cast<int>(round(sixthParts));
0177     int fractional = roundedSixthParts % 6;
0178     if (fractional == 0 || abs(sixthParts - roundedSixthParts) > 1e-3) {
0179         return i18nc("Exposure bias/compensation in exposure value (EV)", "%1 EV", locale.toString(val, 'g', 3));
0180     }
0181     int integral = roundedSixthParts / 6;
0182     int nominator = fractional;
0183     int denominator = 6;
0184     if (nominator % 2 == 0) {
0185         nominator = nominator / 2;
0186         denominator = denominator / 2;
0187     } else if (nominator % 3 == 0) {
0188         nominator = nominator / 3;
0189         denominator = denominator / 3;
0190     }
0191     if (integral != 0) {
0192         return i18nc("Exposure compensation given as integral with fraction, in exposure value (EV)",
0193                      "%1 %2/%3 EV", locale.toString(integral), locale.toString(abs(nominator)), locale.toString(denominator));
0194     }
0195     return i18nc("Exposure compensation given as rational, in exposure value (EV)",
0196                  "%1/%2 EV",  locale.toString(nominator), locale.toString(denominator));
0197 }
0198 
0199 QString FormatStrings::formatAspectRatio(const QVariant& value)
0200 {
0201     return i18nc("Aspect ratio, normalized to one", "%1:1", QLocale().toString(round(value.toDouble() * 100) / 100));
0202 }
0203 
0204 QString FormatStrings::formatAsFNumber(const QVariant& value)
0205 {
0206     return i18nc("F number for photographs", "f/%1", QLocale().toString(value.toDouble(), 'g', 2));
0207 }