[Icons] Render authored theme SVG art without the SVG image plugin

`QPixmap("theme:file.svg")` needs Qt's SVG *image-format* plugin. Debian
builds that as the separate `qt6-svg-plugins` package, which is only a
Recommends of `qt6-svg-dev`, so a `--no-install-recommends` install (CI,
minimal user installs) does not have it. Authored theme art then loaded as
a null pixmap: the two authored-art render tests failed on Debian 13 and on
the Ubuntu sanitize leg while passing on a developer machine whose Qt
bundles the format.

Render authored SVG assets through QSvgRenderer instead, which is already
linked for the sentinel path and needs no plugin. Raster assets keep the
QPixmap loader. The test suite now hides the plugin directories, so the
dependency cannot come back unnoticed on platforms that ship the plugin.
This commit is contained in:
Lukas Brübach 2026-11-06 22:45:32 +01:00
parent 876d66ffc9
commit ceef749ac3
2 changed files with 71 additions and 3 deletions

View file

@ -8,6 +8,7 @@
#include <QApplication>
#include <QByteArray>
#include <QColor>
#include <QCoreApplication>
#include <QDir>
#include <QFile>
#include <QFont>
@ -478,6 +479,61 @@ TEST_F(ThemeIconRenderTest, SentinelSvgBadgeMatchesTheEngine)
EXPECT_GT(countOpaquePixels(pixmap, ThemeIconBadge::glyphColor(tone)), 0);
}
/**
* @brief Hides Qt's plugin directories while it lives, then restores them.
*
* Only the image-format plugins matter here: the platform plugin the fixture
* needs is already loaded by the time a test runs.
*/
class ScopedPluginPathHiding
{
public:
ScopedPluginPathHiding() : saved(QCoreApplication::libraryPaths())
{
QCoreApplication::setLibraryPaths({});
}
~ScopedPluginPathHiding()
{
QCoreApplication::setLibraryPaths(saved);
}
ScopedPluginPathHiding(const ScopedPluginPathHiding &) = delete;
ScopedPluginPathHiding &operator=(const ScopedPluginPathHiding &) = delete;
private:
QStringList saved;
};
/**
* @brief Authored SVG art must not depend on Qt's SVG image-format plugin.
*
* Loading an `.svg` through QPixmap needs the separate `qsvg` image plugin.
* Distributions that build Qt with the plugin split out (Debian, and therefore
* CI) ship it in its own package, and a `--no-install-recommends` install does
* not have it, so authored theme art rendered blank there while working on a
* developer machine whose Qt bundles the format. Hiding the plugin directories
* reproduces that install.
*/
TEST_F(ThemeIconRenderTest, AuthoredArtDoesNotNeedTheSvgImagePlugin)
{
QTemporaryDir themeDir;
ASSERT_TRUE(themeDir.isValid());
QFile file(QDir(themeDir.path()).absoluteFilePath(QStringLiteral("plain.svg")));
ASSERT_TRUE(file.open(QIODevice::WriteOnly));
file.write(svgWith("#336699"));
file.close();
QDir::addSearchPath(QStringLiteral("theme"), themeDir.path());
// The offscreen platform plugin is already loaded by the fixture, so an
// empty library path only removes the image-format plugins.
const ScopedPluginPathHiding hidePlugins;
const QIcon icon = loadThemedIcon(QStringLiteral("plain"));
const QPixmap pixmap = icon.pixmap(QSize(32, 32));
ASSERT_FALSE(pixmap.isNull()) << "authored SVG art went through the QPixmap loader";
EXPECT_GT(countOpaquePixels(pixmap, QColor(QStringLiteral("#336699"))), 0);
}
/** @brief Badge never paints authored art: the asset keeps its own colors. */
TEST_F(ThemeIconRenderTest, BadgeOnAuthoredArtUsesTheAssetPath)
{