resvg for SVGs in Qt

People keep lamenting how lackluster Qt’s SVG renderer is. It leads to poorly rendered icons and wallpapers and it mostly only implements the SVG Tiny specification. As a weekend project I put together a resvg based image handler replacement. It was super easy because resvg is amazing!

The Plugins

An application that uses QSvgRenderer actually can, for the most part, just use resvg’s Qt header and call it a day. It has a very convenient interface that lets you replace most uses of QSvgRenderer with resvg’s. That’s all lovely, except who wants to port every application to a new library.

Luckily, most applications don’t actually use QSvgRenderer directly, they use it through 2 plugins. The iconengine plugin, providing a QIconEngine. And the imageformats plugin, providing a QImageIOHandler. Understanding when which is used is the first step.

Duh! The icon engine is used for icons! – Well, yes, usually…

The SVG QIconEngine confusingly isn’t actually used on KDE Plasma, instead we route all icon lookups through KIconEngine which does some magic. Ah! But where does it then get the icon from? If we follow the code paths a bit we end up here and after some reading on QImageReader we learn that on KDE Plasma icon lookup is actually not running through the SVG iconengine plugin but the imageformats plugin. Slightly confusing but actually beneficial because it means that icon rendering is ultimately running through the same code paths as regular SVG image reading when e.g. loading an Image in QML.

By creating an imageformats plugin we can replace most uses of QSvgRenderer with resvg without having to touch every application. Hooray!

QImageIOHandler

And the good news keeps on coming. A basic QImageIOHandler is super easy to implement.

    bool canRead() const override;
    bool read(QImage *image) override;

All we need to do is implement reading

    *image = ResvgRenderer(device()->readAll(), ResvgOptions()).renderToImage();

That gets us a working plugin. There are some extra features one can and should implement but for the most part that is all that’s needed. You can check out the complete source to see where things are at.

Duel of Plugins

Now that we have a plugin we just need to make Qt actually use ours instead of its own. Unfortunately this is where our luck runs out. Qt appears to have no facilities for manipulating which imageformats plugin is used when there are multiple candidates for the same format. In our case we have qsvg and qresvg both supporting SVG and it appears undefined behavior which of the two gets used. So, for the time being we’ll have to overwrite qsvg to get our plugin to reign supreme. Somewhat unfortunate.

Conclusion

Plugins are cool. Resvg is also cool. Making a resvg plugin is double cool.

You can give it a try at https://invent.kde.org/sitter/qresvgimageiohandler

Here’s what it looks like. On the left hand side you can see the image from a bug report where the image viewer icon is misrendered, on the right you can see the same icon rendered correctly using resvg.

To discuss this blog post head on over to KDE Discuss.