File indexing completed on 2024-05-12 03:55:45

0001 /*
0002     SPDX-FileCopyrightText: 2014 Harald Fernengel <harry@kdevelop.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0005 */
0006 
0007 /* This tool converts an svg to a Mac OS X icns file.
0008  * Note: Requires the 'iconutil' Mac OS X binary
0009  */
0010 
0011 #include <QCommandLineParser>
0012 #include <QFileInfo>
0013 #include <QProcess>
0014 #include <QTemporaryDir>
0015 
0016 #include <QGuiApplication>
0017 #include <QPainter>
0018 #include <QStandardPaths>
0019 #include <QSvgRenderer>
0020 
0021 #include "../../kiconthemes_version.h"
0022 
0023 #include <stdio.h>
0024 
0025 /* clang-format off */
0026 #define EXIT_ON_ERROR(isOk, ...) \
0027     do { \
0028         if (!(isOk)) { \
0029             fprintf(stderr, __VA_ARGS__); \
0030             return 1; \
0031         } \
0032     } while (false);
0033 /* clang-format on */
0034 
0035 static bool writeImage(QSvgRenderer &svg, int size, const QString &outFile1, const QString &outFile2 = QString())
0036 {
0037     QImage out(size, size, QImage::Format_ARGB32);
0038     out.fill(Qt::transparent);
0039 
0040     QPainter painter(&out);
0041     svg.setAspectRatioMode(Qt::KeepAspectRatio);
0042     svg.render(&painter);
0043     painter.end();
0044 
0045     if (!out.save(outFile1)) {
0046         fprintf(stderr, "Unable to write %s\n", qPrintable(outFile1));
0047         return false;
0048     }
0049     if (!outFile2.isEmpty() && !out.save(outFile2)) {
0050         fprintf(stderr, "Unable to write %s\n", qPrintable(outFile2));
0051         return false;
0052     }
0053 
0054     return true;
0055 }
0056 
0057 int main(int argc, char *argv[])
0058 {
0059     QGuiApplication app(argc, argv);
0060 
0061     app.setApplicationName(QStringLiteral("ksvg2icns"));
0062     app.setApplicationVersion(QStringLiteral(KICONTHEMES_VERSION_STRING));
0063     QCommandLineParser parser;
0064     parser.setApplicationDescription(app.translate("main", "Creates an icns file from an svg image"));
0065     parser.addPositionalArgument("iconname", app.translate("main", "The svg icon to convert"));
0066     parser.addHelpOption();
0067 
0068     parser.process(app);
0069     if (parser.positionalArguments().isEmpty()) {
0070         parser.showHelp();
0071         return 1;
0072     }
0073 
0074     bool isOk;
0075 
0076     // create a temporary dir to create an iconset
0077     QTemporaryDir tmpDir(QDir::tempPath() + QStringLiteral("/ksvg2icns"));
0078 
0079     isOk = tmpDir.isValid();
0080     EXIT_ON_ERROR(isOk, "Unable to create temporary directory\n");
0081 
0082     const QString outPath = tmpDir.filePath(QStringLiteral("out.iconset"));
0083 
0084     isOk = QDir(tmpDir.path()).mkpath(outPath);
0085     EXIT_ON_ERROR(isOk, "Unable to create out.iconset directory\n");
0086 
0087     const QStringList &args = app.arguments();
0088     EXIT_ON_ERROR(args.size() == 2, "Usage: %s svg-image\n", qPrintable(args.value(0)));
0089 
0090     const QString &svgFileName = args.at(1);
0091 
0092     // open the actual svg file
0093     QSvgRenderer svg;
0094     isOk = svg.load(svgFileName);
0095     EXIT_ON_ERROR(isOk, "Unable to load %s\n", qPrintable(svgFileName));
0096 
0097     // The sizes are from:
0098     // https://developer.apple.com/library/mac/documentation/GraphicsAnimation/Conceptual/HighResolutionOSX/Optimizing/Optimizing.html
0099 
0100     struct OutFiles {
0101         int size;
0102         QString out1;
0103         QString out2;
0104     };
0105 
0106     // create the pngs in various resolutions
0107     const OutFiles outFiles[] = {{1024, outPath + QStringLiteral("/icon_512x512@2x.png"), QString()},
0108                                  {512, outPath + QStringLiteral("/icon_512x512.png"), outPath + QStringLiteral("/icon_256x256@2x.png")},
0109                                  {256, outPath + QStringLiteral("/icon_256x256.png"), outPath + QStringLiteral("/icon_128x128@2x.png")},
0110                                  {128, outPath + QStringLiteral("/icon_128x128.png"), QString()},
0111                                  {64, outPath + QStringLiteral("/icon_32x32@2x.png"), QString()},
0112                                  {32, outPath + QStringLiteral("/icon_32x32.png"), outPath + QStringLiteral("/icon_16x16@2x.png")},
0113                                  {16, outPath + QStringLiteral("/icon_16x16.png"), QString()}};
0114 
0115     for (const OutFiles &outFile : outFiles) {
0116         isOk = writeImage(svg, outFile.size, outFile.out1, outFile.out2);
0117         if (!isOk) {
0118             return 1;
0119         }
0120     }
0121 
0122     // convert the iconset to icns using the "iconutil" command
0123     const QString iconutilExec = QStandardPaths::findExecutable(QStringLiteral("iconutil"));
0124     if (iconutilExec.isEmpty()) {
0125         EXIT_ON_ERROR(false, "Could not find iconutil executable in PATH.\n");
0126     }
0127 
0128     const QString outIcns = QFileInfo(svgFileName).baseName() + QStringLiteral(".icns");
0129 
0130     const QStringList utilArgs = QStringList() << "-c"
0131                                                << "icns"
0132                                                << "-o" << outIcns << outPath;
0133 
0134     QProcess iconUtil;
0135 
0136     iconUtil.start(iconutilExec, utilArgs, QIODevice::ReadOnly);
0137     isOk = iconUtil.waitForFinished(-1);
0138     EXIT_ON_ERROR(isOk, "Unable to launch iconutil: %s\n", qPrintable(iconUtil.errorString()));
0139 
0140     EXIT_ON_ERROR(iconUtil.exitStatus() == QProcess::NormalExit, "iconutil crashed!\n");
0141     EXIT_ON_ERROR(iconUtil.exitCode() == 0, "iconutil returned %d\n", iconUtil.exitCode());
0142 
0143     return 0;
0144 }