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 }