File indexing completed on 2024-05-19 04:35:50

0001 // Original python code: https://github.com/Huluti/Curtail/blob/master/src/compressor.py
0002 // SPDX-FileCopyrightText: 2019 Hugo Posnic
0003 // SPDX-FileCopyrightText: 2023 Carl Schwan <carlschwan@kde.org>
0004 // SPDX-License-Identifier: GPL-3.0-or-later
0005 
0006 #include "optimizer.h"
0007 #include "config.h"
0008 #include "consolelog.h"
0009 
0010 #include <QCoroProcess>
0011 #include <QFile>
0012 #include <QFileInfo>
0013 #include <QProcess>
0014 
0015 using namespace Qt::Literals::StringLiterals;
0016 
0017 QCoro::Task<void> optimizeJpeg(const Config *config, const ImageInfo &image, ConsoleLog *log)
0018 {
0019     QStringList params1;
0020     QStringList params2;
0021 
0022     if (config->safeMode()) {
0023         params1 = {
0024             u"--max="_s + QString::number(config->jpgLossyLevel()),
0025             u"-o"_s,
0026             u"-f"_s,
0027             u"--stdout"_s,
0028             image.path.toLocalFile(),
0029         };
0030         params2 = {
0031             u"-o"_s,
0032             u"-f"_s,
0033             u"--stdout"_s,
0034             image.path.toLocalFile(),
0035         };
0036     } else {
0037         params1 = {
0038             u"--max="_s + QString::number(config->jpgLossyLevel()),
0039             u"-o"_s,
0040             u"-f"_s,
0041             image.path.toLocalFile(),
0042         };
0043         params2 = {
0044             QStringLiteral("-o"),
0045             QStringLiteral("-f"),
0046             image.path.toLocalFile(),
0047         };
0048     }
0049 
0050     if (config->jpgProgressive()) {
0051         params1.prepend(QStringLiteral("--all-progressive"));
0052         params2.prepend(QStringLiteral("--all-progressive"));
0053     }
0054 
0055     if (!config->keepMetadata()) {
0056         params1.prepend(QStringLiteral("--strip-all"));
0057         params2.prepend(QStringLiteral("--strip-all"));
0058     }
0059 
0060     QProcess proc;
0061     auto process = qCoro(proc);
0062 
0063     if (config->safeMode()) {
0064         co_await process.start(u"jpegoptim"_s, config->jpgLossless() ? params1 : params2);
0065         co_await process.waitForFinished();
0066         QFile newFile(image.newPath.toLocalFile());
0067         if (newFile.open(QIODeviceBase::WriteOnly)) {
0068             newFile.write(proc.readAllStandardOutput());
0069             newFile.close();
0070         }
0071     } else {
0072         co_await process.start(u"jpegoptim"_s, config->jpgLossless() ? params1 : params2);
0073         co_await process.waitForFinished();
0074     }
0075 
0076     log->addConsoleEntry(QString::fromUtf8(proc.readAllStandardError()), image.path.toLocalFile());
0077 }
0078 
0079 QCoro::Task<void> optimizePng(const Config *config, const ImageInfo &image, ConsoleLog *log)
0080 {
0081     QProcess proc;
0082     auto process = qCoro(proc);
0083 
0084     QString file = image.path.toLocalFile();
0085     if (config->safeMode()) {
0086         QFile::copy(image.path.toLocalFile(), image.newPath.toLocalFile());
0087         file = image.newPath.toLocalFile();
0088     }
0089     QStringList oxipngArguments = {
0090         u"-o"_s,
0091         QString::number(config->pngLosslessLevel()),
0092     };
0093 
0094     if (!config->keepMetadata()) {
0095         oxipngArguments.append(u"--strip"_s);
0096         oxipngArguments.append(u"safe"_s);
0097     }
0098 
0099     oxipngArguments.append(file);
0100     co_await process.start(QStringLiteral("oxipng"), oxipngArguments);
0101 
0102     co_await process.waitForFinished();
0103     const QByteArray outputUtf8 = proc.readAllStandardError();
0104     const auto output = QString::fromUtf8(outputUtf8);
0105 
0106     log->addConsoleEntry(output, image.path.toLocalFile());
0107 }
0108 
0109 QCoro::Task<void> optimizeSvg(const Config *config, const ImageInfo &image, ConsoleLog *log)
0110 {
0111     QProcess proc;
0112     auto process = qCoro(proc);
0113 
0114     QString newPath = image.newPath.toLocalFile();
0115     if (!config->safeMode()) {
0116         newPath += u".temp"_s;
0117     }
0118 
0119     QStringList arguments;
0120     if (config->svgMaximumLevel()) {
0121         arguments = {u"-i"_s,
0122                      image.path.toLocalFile(),
0123                      u"-o"_s,
0124                      newPath,
0125                      u"--enable-viewboxing"_s,
0126                      u"--enable-id-stripping"_s,
0127                      u"--enable-comment-stripping"_s,
0128                      u"--shorten-ids"_s,
0129                      u"--indent=none"_s};
0130     } else {
0131         arguments = {
0132             u"-i"_s,
0133             image.path.toLocalFile(),
0134             u"-o"_s,
0135             newPath,
0136         };
0137     }
0138 
0139     co_await process.start(u"scour"_s, arguments);
0140     co_await process.waitForFinished();
0141 
0142     if (!config->safeMode()) {
0143         QFile newPathFile(newPath);
0144         newPathFile.rename(image.path.toLocalFile());
0145     }
0146 
0147     log->addConsoleEntry(QString::fromUtf8(proc.readAllStandardOutput()), image.path.toLocalFile());
0148 }
0149 
0150 QCoro::Task<void> optimizeWebp(const Config *config, const ImageInfo &image, ConsoleLog *log)
0151 {
0152     QProcess proc;
0153     auto process = qCoro(proc);
0154 
0155     QStringList arguments = {image.path.toLocalFile()};
0156 
0157     QString newPath = image.newPath.toLocalFile();
0158     if (!config->safeMode()) {
0159         newPath += u".temp"_s;
0160     }
0161 
0162     if (!config->keepMetadata()) {
0163         arguments.append(u"-metadata"_s);
0164         arguments.append(u"all"_s);
0165     }
0166 
0167     int quality = config->webpLossyLevel();
0168     if (config->webpLossless()) {
0169         arguments.append(u"-lossless"_s);
0170         quality = 100;
0171     }
0172 
0173     // multithreaded
0174     arguments.append(u"-mt"_s);
0175 
0176     // (lossless) compression mode
0177     arguments.append(u"-m"_s);
0178     arguments.append(QString::number(config->webpLosslessLevel()));
0179 
0180     // quality
0181     arguments.append(u"-q"_s);
0182     arguments.append(QString::number(quality));
0183 
0184     // output
0185     arguments.append(u"-o"_s);
0186     arguments.append(newPath);
0187 
0188     co_await process.start(u"cwebp"_s, arguments);
0189     co_await process.waitForFinished();
0190 
0191     if (!config->safeMode()) {
0192         QFileInfo fileInfo(image.path.toLocalFile());
0193         QFileInfo fileInfoNew(newPath);
0194 
0195         if (fileInfo.size() < fileInfoNew.size()) {
0196             co_return;
0197         }
0198 
0199         QFile newPathFile(newPath);
0200         newPathFile.rename(image.path.toLocalFile());
0201     }
0202 
0203     log->addConsoleEntry(QString::fromUtf8(proc.readAllStandardError()), image.path.toLocalFile());
0204 }