1// Copyright (C) 2025 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3
4#include <QGuiApplication>
5#include <QQmlApplicationEngine>
6#include <QCommandLineParser>
7#include <QFile>
8#include <QQuickWindow>
9#include <QQuickItem>
10#include <QtQuickVectorImageGenerator/private/qquickitemgenerator_p.h>
11#include <QtQuickVectorImageGenerator/private/qquickqmlgenerator_p.h>
12#include <QtQuickVectorImageGenerator/private/qquickvectorimageglobal_p.h>
13#include <QtLottie/private/qlottieroot_p.h>
14#include <QtLottieVectorImageGenerator/private/qlottievisitor_p.h>
15
16#define ENABLE_GUI
17
18int main(int argc, char *argv[])
19{
20#ifdef ENABLE_GUI
21 QGuiApplication app(argc, argv);
22#else
23 QCoreApplication app(argc, argv);
24#endif
25
26 QCommandLineParser parser;
27 parser.setApplicationDescription("Lottie to QML converter [tech preview]");
28 parser.addHelpOption();
29 parser.addPositionalArgument(name: "input", description: QCoreApplication::translate(context: "main", key: "Lottie file to read."));
30 parser.addPositionalArgument(name: "output", description: QCoreApplication::translate(context: "main", key: "QML file to write."), syntax: "[output]");
31
32 QCommandLineOption curveRendererOption({ "c", "curve-renderer" },
33 QCoreApplication::translate(context: "main", key: "Use the curve renderer in generated QML."));
34 parser.addOption(commandLineOption: curveRendererOption);
35
36 QCommandLineOption optimizeOption({ "p", "optimize-paths" },
37 QCoreApplication::translate(context: "main", key: "Optimize paths for the curve renderer."));
38 parser.addOption(commandLineOption: optimizeOption);
39
40 QCommandLineOption typeNameOption({ "t", "type-name" },
41 QCoreApplication::translate(context: "main", key: "Use <typename> for Shape."),
42 QCoreApplication::translate(context: "main", key: "typename"));
43 parser.addOption(commandLineOption: typeNameOption);
44
45 QCommandLineOption copyrightOption("copyright-statement",
46 QCoreApplication::translate(context: "main", key: "Add <string> as a comment at the start of the generated file."),
47 QCoreApplication::translate(context: "main", key: "string"));
48 parser.addOption(commandLineOption: copyrightOption);
49
50 QCommandLineOption assetOutputDirectoryOption("asset-output-directory",
51 QCoreApplication::translate(context: "main", key: "If the Lottie file refers to external or embedded files, such as images, these "
52 "will be copied into the same directory as the output QML file by default. "
53 "Set the asset output directory to override the location."),
54 QCoreApplication::translate(context: "main", key: "directory"));
55 parser.addOption(commandLineOption: assetOutputDirectoryOption);
56
57 QCommandLineOption assetOutputPrefixOption("asset-output-prefix",
58 QCoreApplication::translate(context: "main", key: "If the Lottie file refers to external or embedded files, such as images, these "
59 "will be copied to files with unique identifiers. By default, the files will be prefixed "
60 "with \"lottie_asset_\". Set the asset output prefix to override the prefix."),
61 QCoreApplication::translate(context: "main", key: "prefix"));
62 assetOutputPrefixOption.setDefaultValue(QLatin1String("lottie_asset_"));
63 parser.addOption(commandLineOption: assetOutputPrefixOption);
64
65 QCommandLineOption keepPathsOption("keep-external-paths",
66 QCoreApplication::translate(context: "main", key: "Any paths to external files will be retained in the QML output. "
67 "The paths will be reformatted as relative to the output file. If "
68 "this is not enabled, copies of the file will be saved to the asset output "
69 "directory. Embedded data will still be saved to files, even if "
70 "this option is set."));
71 parser.addOption(commandLineOption: keepPathsOption);
72
73#ifdef ENABLE_GUI
74 QCommandLineOption guiOption({ "v", "view" },
75 QCoreApplication::translate(context: "main", key: "Display the generated QML in a window. This is the default behavior if no "
76 "output file is specified."));
77 parser.addOption(commandLineOption: guiOption);
78#endif
79 parser.process(app);
80 const QStringList args = parser.positionalArguments();
81 if (args.size() < 1) {
82 parser.showHelp(exitCode: 1);
83 }
84
85 const QString inFileName = args.at(i: 0);
86
87 QString commentString = QLatin1String("Generated from Lottie file %1").arg(args: inFileName);
88 const QString importString = QLatin1String("Qt.labs.lottieqt.VectorImageHelpers");
89
90 const auto outFileName = args.size() > 1 ? args.at(i: 1) : QString{};
91 const auto typeName = parser.value(option: typeNameOption);
92 const auto assetOutputDirectory = parser.value(option: assetOutputDirectoryOption);
93 const auto assetOutputPrefix = parser.value(option: assetOutputPrefixOption);
94 const bool keepPaths = parser.isSet(option: keepPathsOption);
95 auto copyrightString = parser.value(option: copyrightOption);
96
97 if (!copyrightString.isEmpty()) {
98 copyrightString = copyrightString.replace(before: "\\n", after: "\n");
99 commentString = copyrightString + u"\n" + commentString;
100 }
101
102 QQuickVectorImageGenerator::GeneratorFlags flags;
103 if (parser.isSet(option: curveRendererOption))
104 flags |= QQuickVectorImageGenerator::GeneratorFlag::CurveRenderer;
105 if (parser.isSet(option: optimizeOption))
106 flags |= QQuickVectorImageGenerator::GeneratorFlag::OptimizePaths;
107
108 QQuickQmlGenerator generator(inFileName, flags, outFileName);
109 generator.setShapeTypeName(typeName);
110 generator.setCommentString(commentString);
111 generator.setAssetFileDirectory(assetOutputDirectory);
112 generator.setAssetFilePrefix(assetOutputPrefix);
113 generator.setRetainFilePaths(keepPaths);
114 generator.addExtraImport(import: importString);
115
116 int frameNo = qEnvironmentVariableIntValue(varName: "QLT_FRAMENO");
117
118 QFile f(inFileName);
119 bool ok = false;
120 QLottieRoot root;
121 if (f.open(flags: QIODevice::ReadOnly)) {
122 QByteArray jsonSource = f.readAll();
123
124 if (!root.parseSource(jsonSource, fileSource: inFileName)) {
125 root.setStructureDumping(true);
126 for (QLottieBase *elem : root.children()) {
127 if (elem->active(frame: frameNo))
128 elem->updateProperties(frame: frameNo);
129 }
130
131 QLottieVisitor visitor(inFileName, &generator);
132 visitor.render(layer: root);
133 ok = generator.save();
134 }
135 }
136
137#if defined(ENABLE_GUI)
138 if (ok && (parser.isSet(option: guiOption) || outFileName.isEmpty())) {
139 app.setOrganizationName("QtProject");
140 const QUrl url(QStringLiteral("qrc:/main.qml"));
141 QQmlApplicationEngine engine;
142 QObject::connect(sender: &engine, signal: &QQmlApplicationEngine::objectCreated,
143 context: &app, slot: [&](QObject *obj, const QUrl &objUrl){
144 if (!obj && url == objUrl)
145 QCoreApplication::exit(retcode: -1);
146 if (obj) {
147 auto *containerItem = obj->findChild<QQuickItem*>(QStringLiteral("svg_item"));
148 QQuickItemGenerator generator(inFileName, flags, containerItem, engine.rootContext());
149 generator.addExtraImport(import: importString);
150
151 QLottieVisitor visitor(inFileName, &generator);
152 visitor.render(layer: root);
153 }
154 });
155 engine.load(url);
156 return app.exec();
157 }
158#endif
159
160 return ok ? 0 : 1;
161}
162

source code of qtlottie/tools/lottietoqml/main.cpp