Qt SVG: Not so 1.2 Tiny any more

Introduction

Qt SVG has been around for basically forever, at least from the perspective of a Millennial like me. It has been a great tool to keep pixel artefacts away from our applications, a trait that has become increasingly important especially on highDPI displays. Unfortunately Qt SVG implements only SVG 1.2 Tiny, which can be limiting in many cases.

In 2015 I defended my Master thesis and I built my presentation with the Qt Graphics View Framework to make something that looked like Prezi but with crisp LaTeX equations. I used LaTeX and QGraphicsSvgItem to power the equation editor of my little presentation application (This was before MathML and MathJax took over the math world). Unfortunately, LaTeX created many <symbol> elements that were not part of SVG 1.2 Tiny and Qt did not want to render it. I helped myself with a pipeline of tools that eventually transformed my files into valid SVG 1.2 Tiny. A rather annoying experience.

I was still lucky, as symbols can be easily replaced with other SVG 1.2 Tiny elements. However, this is not always the case, especially with <mask>, <pattern> and <filter>, which are SVG (not SVG 1.2 Tiny) elements that cannot be emulated easily. To make things worse, these are very common elements that are used by many applications and designers without considering standards or their versions. In fact, even QSvgGenerator creates masks and patterns to augment the capabilities of QPainter. In short, many SVGs that cross the path of Qt SVG contain something beyond SVG Tiny and our SVG renderer is probably the sole defender of a strict SVG Tiny 1.2 standard.

New Features

In this situation it does not make much sense to stick to the standard. So when our chief-maintainer asked me to implement selected useful elements beyond SVG Tiny 1.2, I was very happy to oblige. Fast forward half a year and another release of Qt (6.7) we now provide support for the following SVG 1.1 (lower version number but no tiny here) or SVG 2.0 elements:
  • <symbol>
  • <marker>
  • <pattern>
  • <mask>
  • <filter>, <feFlood>, <feGaussianBlur>, <feColorMatrix>, <feOffset>, <feMerge> and <feComposite>
We added these elements not to be compliant with SVG 1.1 or 2.0, but to deal with the most common stuff our users see. We limit our self to the most popular use cases and in particular to static elements. Please keep that in mind when testing our newest SVG renderer. If you think we missed something elemental, feel free to reach out to us or comment here. Last but not least there is the Qt Webengine module that aims to be compliant with SVG 2.0, in case you need the full standard. Qt SVG should stay a lightweight alternative to it.

Let us look at some stuff we can do since Qt 6.7:
 
First of all, we can do equations, thanks to the <symbol> element:
Your browser: Qt 6.7:
symbol symbol
 
 
Similar to the <symbol> element is the <marker> element. It allows us to decorate a path:
Your browser: Qt 6.7:
marker marker

 

We can now also fill objects with a pattern, thanks to the <pattern> element:
Your browser: Qt 6.7:
pattern pattern-1

 

The <mask> element does what a mask should do and gives designer a lot of flexibility:
Your browser: Qt 6.7:
mask mask

 

The <filter> element can contain many filter primitives, that are applied in consecutive order. I counted 19 filter primitives in SVG 1.1, and as of Qt 6.7 we support six of them.

With the supported <feOffset>, <feGaussianBlur> and <feMerge> we can draw Shadow effects:
Your browser: Qt 6.7:
blur blur

 

The filter <feColorMatrix> allows to exchange color:
Your browser: Qt 6.7:
colormatrix colormatrix

 

And finally, <feComposite> enables rather complex compositions.
Your browser: Qt 6.7:
feComposite feComposite

Final Notes

Not everything in these elements is supported. For example, we do not support stroke="context-stroke" or  fill="context-fill"  in the marker element, but neither does Chromium. We are also limited in terms of color spaces and only support color-interpolation-filters="sRGB". The filters can not use the background as input and the Gaussian blur filter looks a bit funky with a rotational transformation in combination with unequal vertical and horizontal derivation (a bug I intend to fix). We are also aware that we miss many filter primitives and one of my personal favorites, <clipPath> (We have a patch available by now). However, with the recent change you should see a huge improvement in rendering everyday SVGs. We are about on the same level, sometimes a bit better, than most viewers I could find on Ubuntu, including Inkscape. We have a new page in our documentation that keeps track of supported and unsupported elements and features.

But it gets even better, as this is just the beginning of a new era for Qt SVG. With this step, we are opening Qt SVG up to support elements beyond SVG Tiny 1.2. This means that we will aim to include useful and common elements from SVG 1.1 and SVG 2.0, if maintenance is reasonably feasible. We will not aim for compliance with these standards but we will keep an eye on feature requests from our users. Further, we are open to contributions of such extensions by the community. So if you miss your favorite SVG element in the list above, fell free to send us some code, preferably on our code review platform.

If you feel that all of this is a mistake and that we should have never left the safe haven of a well-defined standard, you can still get back the old behavior: Just set the new flag SvgRenderer::options to include QtSvg::Tiny12FeaturesOnly and our parser and renderer will ignore everything that is not included in SVG 1.2 Tiny.

Blog Topics:

Comments