Skip to content

Friday, 25 April 2025

After tackling fonts and opentype features thoroughly, I decided to continue on with taking care of font metrics. This covered font size, line height, baseline alignment and shift, as well as text underlines and carets.

What are font metrics.

If a font is a designed collection of glyphs, then font metrics are the specifications to which those glyphs are designed. Well, some of them at the least. While there are ways to store stem thickness metadata into the font, in practice the font metrics that are most used are those used to lay out lines of text, and to determine the font size.

The main one of these is the EM size. This defines the coordinate system in which the glyphs are drawn. When setting a font size, you end up scaling the em-size to the font size value.

To place the origin of that coordinate system, the ascender and descender are defined. Usually, ascender plus descender equals em-size, but as we’ll discuss later, that’s not self-evident.

Beyond that, there’s often extra metrics stored like the x-height, cap-height, and super- and subscript positions.

The Latin text, "Abg" annotated with its common typographic metrics: Ascender, Cap-height, X-height, baseline and descender.

But up till now, we’ve only discussed metrics that are mainly common in European scripts. Other writing systems have other metrics, and while some of these metrics can map to the European metrics, others, like em-box center and hanging baseline are unique to those writing systems.

Several samples of Devanagari and Tibetan, aligned by their head stroke.

North Brahmic scripts like Devanagari have a headstroke that glyphs of different sizes are aligned at.

The Han ideograph for water, annotated with its metrics. The outer square is the em-box, while the inner square that touches the glyph is the character face. A blue orthogonal cross goes through the square, showing the central lines.

Meanwhile, CJK ideographs are designed inside an Em-box, which itself contains a character face-square. The latter is usually used for a sort of optical alignment of CJK ideographs. The embox is used to calculate the centers, which in turn is used to align the character vertically. Horizontally, each of these points, plus the Latin baseline, could be used to align the text.

Various instances of the han ideograph for water at different sizes being aligned by the top, center and bottom, as well as the same ideographs aligned to the Latin text "Abg".

These too, can be stored inside the font, and need special attention how to use them within text layout.

Finally, text underlines and strike-throughs, as well as slanted carets have extra metrics. We’ll touch upon these at the end.

Font size and line height.

So, as mentioned before, the font size is the value that the em value the font was designed at is scaled to. So if a font has an em size of 1000, then that means all glyphs in that font are designed in a coordinate system relative to 1000. If you want to scale them to, say, 12px, you are effectively scaling all the glyphs’s coordinates by 12/1000 and then rendering the result to a raster bitmap. Bitmap fonts work a little different, and there you will need to see which of the available bitmap sizes (‘strikes’) is closest to the requested size. In the case of bitmap emoji fonts, it is typical to find the closest size and then scale them to the required size, but for non-color bitmap fonts I made Krita follow Qt; and apply no scaling.

Now, there’s a bit of a problem here: Freetype doesn’t allow setting the point size below 1, so you need to scale the resulting glyphs if you want to be able to have it below 1pt. However, while said scaling of color emoji is done via FreeType transforms, and Raqm handles and applies those transforms, I can’t get it to work for the below 1pt transforms. My suspicion here is that the scaling messes too much with the freetype coordinates which are stored in integers (64x the pixel size), so I had to apply that transform separately once we go from Freetype coordinates into floating point land. Not very happy about that, because that means two separate transforms for basically the same thing (getting proper font size).

Line height

So, line height is the height of the lines. Those of us that have done word processing before will know that it is not unusual for a manuscript style guide to say: “Double spacing”, meaning there’s a full line’s worth of white space between lines (useful to write all those editorial notes into!), while final texts often are at 1.2 to 1.8 spacing.

But then, what should the line height be? Is it a multiple of the font-size? Or whatever the ascender and descender add up to? Because those two things are not the same thing!

But actually what is the ascender and descender? Is it the same as the maximum and minimum extend? It seems that early text layout programmers thought so, because for the longest time, the OpenType values called “WinAscent” and “WinDescent” both served as the ascender, descender and the clipping region on Windows. Apple instead used the values ascent and descent from the hhea and vhea OpenType tables.

Now, thankfully, the CSS specs define the metric we need to use: sTypoAscender and sTypoDescender from the OS/2 table (the fifth way of selecting these values, for those keeping count!), regardless of whether any related bits are set. This is what most implementations use… Except Apple, which uses hhea and vhea still, even within their web browser.

Now then, we’ve got the ascent and descent for horizontal. But what should we use for vertical? The vhea table values? CSS-inline-3 says nothing about the vertical line-height, but thankfully, the SVG 2.0 spec does:

Within an OpenType font, for horizontal writing-modes, the ascent and descent are given by the sTypoAscender and sTypoDescender entries in the OS/2 table. For vertical writing-modes, the descent (the distance, in this case from the (0,0) point to the left edge of the glyph) is normally zero because the (0,0) point is on the left edge. The ascent for vertical writing-modes is either 1 em or is specified by the ideographic top baseline value in the OpenType Base table for vertical writing-modes.

That’s right, a sixth way of selecting ascender and descender!

Still, it is coherent with the way that the OpenType specs want us to calculate the em box (more on that later), where the height of the box should use sTypo values (if the baseline values are missing) and the width should use the baseline values.

There is a small annoying thing here with that vertical-writing modes tends to mean writing-mode: vertical-lr and vertical-rl, regardless of text-orientation, but I suspect that this is intended for the situation of text-orientation: upright, or mixed resolved to upright, because otherwise vertical but rotated text is not going to lay out correctly (For vertically oriented upright text this makes perfect sense).

Units

Another thing that needed tackling was the unit to set CSS lengths with. Many properties inside CSS text can use other units besides the default. Most interesting here are the percentages, but for something like tab size, you can choose to use a multiple of spaces.

Now, within Krita, all vector coordinates are in points, not pixels (see the appendix for their difference), and we keep track of a per document DPI to calculate conversions between the two. This is pretty standard for image editing applications like ours.

SVG mostly facilitates this: An SVG user-unit is explicitly not defined as anything particular, and the difference between the coordinate system set to the viewbox and the width and height set on the top level SVG element is what defines the scaling it’ll experience when used in an HTML context.

Except, CSS length values as used by SVG text do have explicit units. And the default user unit for those is CSS pixels. This is throughout all of CSS, so if the specs say “you can use an SVG path here”, then the coordinates of that SVG path are assumed to be pixels (especially relevant because SVG path definition does not allow units to be defined).

For us, that means we cannot actually store any of the absolute CSS values, that is, mm, inch, pixel, but instead convert everything to point under the hood and then store that as SVG units, and set the viewport and width/height accordingly. This mostly works, except with the line-height and tab-size values, which allow a number in addition to a length with unit. Which means those need to be set to px explicitly in those cases.

Ideally, we’d have a way to define the default CSS unit for unit-less situations like SVG, so that for our case, we could write line-height: 12pt; font-size:12;, and the parser would be able to understand that font-size is the same value as line-height here.

However, because we are converting everything to points, we can afford to offer pixels to be actual document pixels, and not the hard-coded-to-96ppi css pixels. This is useful because a number of our users don’t understand the difference between points and pixels, or PPI in itself. In the latter case, those tend to set the PPI to the pixel width, meaning that the document is exactly one Postscript point wide, and we get a bug report that the text is too large. For those artists, being able to divert them to use pixels might be a good solution (and to point out that their PPI value is useless). Then there’s of course the artists that want to use retro-style fonts for aesthetic purposes, and yes, pixels is useful there, but admittedly that also needs a little bit more work beyond the scope of this particular blog post.

Relative units.

There’s more to CSS values than absolute units. It has relative units. In particular, font-relative units. These units use metrics from inside the font to determine their resolved value. Think of the height of a small x, or the capital height. I in particular wanted to get these to work because eventually the idea is to have style presets, and being able to store something like “font-size: 0.5em” as a preset seems mighty useful.

For these (and the baselines later), I collect them inside a metrics struct. These are then used before text layout begins proper to handle inheritance and convert all such units to points.

Percentages are a separate thing from font-relative units, and are a bit weird. In SVG 1.1, percentages were largely relative to the root viewport, which is somewhat odd, but it is what the spec requires. In SVG 2.0, most of them are now linked to their CSS reference values. In the case of text-indent in particular, the inline-size of the wrapping area needs to be used to determine the percentage value, so that one is resolved during line layout.

Font Size Adjust

So, because the font-size is linked to a somewhat arbitrary value that is part of the design of the font, it can be useful to set the font-size by a more tangible metric.

Font size adjust allows just this, and is meant in particular to ensure that the given metric is at a decent size. CSS font-3 and 4 only allow setting the proportion of the x-height, which in turn is valuable because that can great affect the readability of the text. The idea is that you want to ensure that the x-height for all fonts is, say, 60% of the font-size. Then, you take font-size-adjust and set it to 0.6. Then during text layout, when the font-size is set, the x-height within the font is used to adjust the total font size. Setting font-size-adjust to 1.0 should then scale the font so that the x-height is the specified css font-size.

CSS-Fonts-5 has a proposal for more of these metrics, but is still very much in draft, so I’ve avoided it for now.

One thing I do want to mention is a bit of a problem: All of the proposed metrics can be adjusted by variable font’s parameters. That is to say, you can have an optical axis on a variable font, that sets the x-height higher on small sizes and lower on big sizes. Then, changing the font-size will change the x-height. This means that you could end up in some kind of cycle trying to get the right font size for the given font-size-adjust.

I don’t know how to fix this. For now, what I do is that I set the size (but not the axes), test against the default x-height, set the adjusted x-height and then set the axes, which will mean that something like optical size is at the very least the size the font is set at, but that may not be the size which was intended by font-size-adjust.

Baseline alignment and friends.

Baseline alignment is all about aligning glyphs to one another perpendicular to the line width. By default, most fonts are set up to align to the same baseline as that of Latin, because this gives the least trouble in mixed-script situations. However, when in single-script situations, especially those with font-fallback, it can look a little messy. Baseline alignment can reduce this messiness:

Devanagari with multiple scripts and font-sizes, the top row aligns it to the Latin baseline, while the bottom row aligns it to the headstroke. Fonts are Noto Sans Devanagari and Sharad76.

Now, of course, it is rather hard to find these baselines, and trickier still to synthesize them right. The Sharad76 font above, for example, is a handwriting font, so it is hard to synthesize a hanging baseline for it. So, OpenType has a place to store these: The BASE table.

According to that spec, the base table is supposed to make it so that glyphs of different scripts and fonts can align correctly. And to do so, it allows for defining, per script and run direction (vertical or horizontal), values for different baseline tags, as well as the preferred baseline for that script, and min-max extents.

Unfortunately, the spec doesn’t specify how the base table is supposed to see the OS/2 and hhea/vhea tables in this system, like, which baseline those are aligned to… This is a bit of a problem for the spec, because it imagines it to be able to have script to be defined by a more natural origin for their glyphs, but without ascender or descender being aligned to anything, it becomes hard to imagine how you’d make an editable text layout with a font like that.

Still, it exists, the font development recommendations suggest that fonts are by default build for Latin baseline. The only implementation I personally know of is Adobe products’ CJK composer, where the ideographic em-box and character face can be aligned against.

Then, there’s four different W3C baseline alignment specifications. The oldest of these is from XSL 1.1, which was then adapted by SVG 1.1, which in turn was then adapted by CSS-inline-3. SVG 2.0 also has some details, but like how SVG 1.1 adapts XSL, SVG 2.0 adapts CSS-inline. The last one is significantly different from the other two, but all of them are rather confusing. So confusing in fact, that I actually wrote this part before finishing up the implementation, just to get a grasp on what I’m actually supossed to implement.

Lets start with the oldest. The main thing XSL’s spec wants, is that there’s a core baseline table for the whole paragraph, and each glyph aligns to its prefered baseline on that root baseline table:

The glyphs determined by the fo:characters that are in the content of the two formatting objects are aligned based on the script to which the glyph belongs.

Baseline alignment as per XSL 1.1: different sizes of text in different scripts align to the same root baseline table by their prefered baseline.

This is why “dominant baseline” doesn’t inherit; every time the dominant baseline is defined, a new table is calculated, and if it is not, the old baseline table is inherited. It’s alignment baseline that allows manual alignment of glyphs.

SVG 1.1 is similar, except that “before-edge” and “after-edge”, which are basically line-relative alignments (lines, which SVG 1.1 doesn’t have), get interpreted to be the equivelant of ascent/descent.

The description of alignment-baseline: auto, however, shows why it is important to read the XSL spec, as the definition in SVG 1.1 is rather confusing, it says literally two different things:

The value is the dominant-baseline of the script to which the character belongs – i.e., use the dominant-baseline of the parent.

While XSL is much clearer here:

The computed value depends on the kind of object on which it is being used. For fo:character, the value is the dominant-baseline of the script to which the character belongs. If the value of the “script” property on the parent formatting object is other than “auto” then use the baseline for that script; otherwise, use the dominant-baseline of the parent. For all other objects, the value is computed as for the “baseline” value.

So basically, align glyphs to the inherited baseline table with their preferred baseline, everything non-glyph is aligned using the dominant baseline.

CSS-inline-3 however, gets rid of this inherited baseline table, and instead says ‘any inline-box is always aligned to its parent’, ‘any glyph is aligned to its parent inline-box’… I think. The actual text says, for dominant-baseline:

For inline boxes, the dominant baseline is used to align the box’s text (and, unless otherwise specified by vertical-align, any inline-level child boxes) by aligning each glyph/box’s corresponding baseline to the box’s own dominant baseline.

For alignment-baseline:

When performing baseline alignment, these values specify which baseline of the box is aligned to the corresponding baseline of its alignment context. (In an inline formatting context, inline-level box fragments and glyphs share an alignment context established by their parent inline box fragment along its inline axis. …)

And finally, a little earlier, in the Baseline Alignment intro text:

For a glyph, the alignment baseline is always determined by the parent’s dominant baseline.

What trips me up here is “parent inline box fragment”, which refers to a box that due layout is split over multiple lines, columns or shapes. But that isn’t really relevant for alignment baseline, because that only has font-derived alignment points. If there were line-derived alignment points, (like, before-edge/after-edge), then this would make sense. But instead those (renamed “top” and “bottom”, and joined by “middle”) are now part of baseline-shift, with a note saying “maybe these should be part of alignment baseline”…

Then there’s this weird note under dominant-baseline:

In SVG text layout, these values instead specify the baseline that is aligned to the SVG current text position.

Which is very confusing to read when you’re trying to write SVG 2 text layout with automated wrapping, because it makes it sound like alignment-baseline will have no effect, in which case, why would it be in the SVG 2 spec? (SVG 2 also doesn’t help here, it kinda glosses over the fact that the implementation details are completely different between CSS-inline-3 and SVG 1.1. Might be because the spec is still not finished…)

Another thing that is different between all the specs, is that in XSL, “use-script” should use the script defined inside the script property (that is, XSL has a property named “script”, not the unicode property script), and then use the default baseline for that script as defined in the font. SVG 1.1 has no script property, so it should guess the dominant script inside the text.

CSS-inline-3 removed the “use-script” property, because the SVG 1.1 definition was too vague, and it seems they don’t know that fonts themselves are supossed to define the default baseline for a script. In theory it can be resurrected, but then using the BCP47 tag, as CSS-text does, and using the default baseline-for-script defined in the font.

Finally, all the specs also define baseline-shift. In CSS-inline-3, it wants to see baseline-shift and alignment-baseline as longhands of vertical-align (XSL wanted to do something similar, by the way).

It then defines the baseline-shift as a post-alignment shift, implying the baseline alignment needs to be done beforehand.

However, XSL and SVG 1.1 have an important note here, with regards to the baseline shift super and sub properties:

The dominant-baseline is shifted to the default position for superscripts. The offset to this position is determined using the font data for the nominal font. Because in most fonts the superscript position is normally given relative to the “alphabetic” baseline, the user agent may compute the effective position for superscripts when some other baseline is dominant. The suggested computation is to subtract the difference between the position of the dominant baseline and the position of the “alphabetic” baseline from the position of the superscript. The resulting offset is determined by multiplying the effective superscript position by the dominant baseline-table font-size. If there is no applicable font data the user agent may use heuristics to determine the offset.

There’s another oddity in the SVG spec, regarding the percentage:

The computed value of the property is this percentage multiplied by the computed “line-height” of the ‘text’ element. The dominant-baseline is shifted in the shift direction (positive value) or opposite to the shift direction (negative value) of the parent text content element by the computed value. A value of “0%” is equivalent to “baseline”.

But SVG 1.1 doesn’t have a line-height, and the spec says to interpret it as font-size. Meaning this property behaves differently between SVG 1.1 and SVG 2 (which does have line-height).

Finally, SVG 2 has some notes as well:

When a different font (or change in font size) is specified in the middle of a run of text, the dominant baseline determines the baseline used to align glyphs in the new font (new size) to those in the previous font. The dominant-baseline property is used to set the dominant baseline.

So then, how should we implement this.

Let’s go back to that SVG note.

In SVG text layout, these values instead specify the baseline that is aligned to the SVG current text position.

SVG 2.0 has a similar note:

SVG uses the value of the dominant-baseline property to align glyphs relative to the ‘x’ and ‘y’ attributes. For the text-orientation value sideways, the auto value for dominant-baseline is alphabetic; however, for backwards compatibility, the glyphs should be aligned to the ‘x’ and ‘y’ attributes using the value central.

So, the “current text position” or “x and y” properties here refer to SVG’s per-character positioning and rotation. One frustration here is that while text-position and x and y refer to similar things, the latter is only a part of the former, with dX, dY and rotation being the remainder.

However, lets assume there’s no dx, dy and rotation, can we then tell what this is trying to do?

Yes, its trying to align the text in question so that its dominant-baseline can align with a path in text-on-path layout. This makes sense and we want this.

Hindi translation of the Krita slogan: “Digital Painting, Creative Freedom”. Aligned with text on path, with the left sample using dominant-baseline: auto, and the right using dominant-baseline: hanging. Sample was made in Inkscape, which supports this behaviour of dominant-baseline already.

Then, it stands to reason that dx, dy and rotation should probably use this new point as well. Which means CSS-inline is correct here; it affects the current text position.

So what we can, in effect, do for dominant-baseline, if it is aligning glyphs, is to have it not specifically align, but rather, have it shift the origin of the glyph’s coordinate system. That’s the glyph path coordinates, bitmap rectangles, as well as all the metrics.

(Now, both the SVG 2 and CSS inline spec caution that for dominant-baseline:auto, the resulting vertical dominant baseline is always central in SVG, but that is largely in regards to text-orientation, and Krita doesn’t support text-orientation right now, so it is not relevant for me yet.)

For CSS text layout (important for wrapping), there’s no difference between aligning the glyphs via dominant baseline or shifting their origin.

Then there’s the question of, how do alignment-baseline and baseline-shift get involved with this?

Firstly, both are non-inheritable. And baseline-shift, or at the least super and sub script positioning can nest. So alignment-baseline, now part of vertical align, then also stacks. Similarly, because vertical-align: super affects the line-height, so will alignment-baseline, now part of vertical-align.

That in turn means that baseline alignment needs to happen before wrapping. Especially as, when wrapping in a shape for SVG 2.0, the line-height will affect how much inline space is available.

Now… there’s a bit of a problem here. The default SVG 2 layout algorithm does this thing to ensure that the initial position aligns with the absolute transforms. Which mean that you can’t start a text with a super-script, as the layout will reset that initial position. Most implementations I’ve checked don’t do this, so the vertical-align probably should be taken into account. However, I can’t really find anything in the SVG 2 spec that confirms this, beyond the following sentence:

As glyphs are sequentially placed along a baseline, the alignment point of a glyph is typically positioned at the current text position (some properties such as vertical-align may alter the positioning).

In addition to no implementation doing this: if you don’t take vertical-align into account, then certain layouts could have the whole text move by removing the first character, which can’t be the intention.

Beyond that, alignment baseline should probably also work from a moved origin, because it needs to compute to “0” when defaulting to the dominant baseline.

Finally, there’s whether or not super and subscript should be adjusted to alignment or dominant baseline. As noted before, XSL and SVG 1.1 say it probably should be aligned to the alphabetic baseline, but there alignment-baseline was the positioning, while dominant-baseline defined the baseline table, while in CSS they’re both a type of positioning behaviour.

In any case something needs to be done, because especially with “hanging”, the behaviour can can change radically whether dominant-baseline is set or not.

The first bit of Devanagari has the smaller portion aligned to the super-script. The second portion has the smaller portion aligned to the super-script, and the larger portion adjusted to the hanging baseline. There’s a semi-transparent version of the larger portion to show how it would look like without dominant baseline adjustment, and thus where the super-script should be if you didn’t adjust the shift for alignment.

Eventually I decided to have alignment-baseline affect the strength of super and sub, because even in a situation like CSS, where the alignment-baseline and baseline-shift are both part of vertical-align, alignment-baseline is always active and aligns to the dominant baseline. Which means that if you have CSS inheriting from two different classes, dominant-baseline can affect super and subscript in unexpected ways.

Finally there’s the line-relative modes. That is baseline-shift: top, centre, bottom (“center” being an absolute terrible name: it means that vertical-align now has “middle”, “central” and “center” meaning three distinct things). I accidentally implemented top and bottom, because I had misread the spec. And I could just parse the baseline-shift: top/bottom and use those instead, but I’m kind of stuck having to decide where to stick them in the UI. The spec itself is saying “well, this is just draft for now”.

What I actually implemented.

Let me run through what Krita is doing now, with some ugly pixel art examples, where each inline-box also has a different text color:

Sequences of ugly pixel art glyphs in Latin, Devanagari and Gurmukhi. Each of them has a orange dot at the alphabetic baseline, and a blue dot at the hanging baseline. There's a grey dot at the superscript position.

Firstly, we have our set of glyphs and their baselines. Each of them has an alphabetic baseline at 0, and a hanging baseline at various points.

Sequence of terribly drawn pixel art glyphs: All are aligned by the blue dot, describing the hanging baseline.

To start with, we translate the coordinate system and all related metrics so that the dominant baseline is the 0 in the coordinate system. When doing this, you will want to adjust the baseline metrics you are storing per-typographic character in the same manner, as that will be useful when calculating the text-decoration later.

Same sequence of ugly pixel art glyphs: Each has been moved up to align within its own box. The translation is marked with yellow, and the length of the translation is written underneath the yellow line.

Then, as the second part of handling dominant baseline alignment, we align all glyphs to their parent inline box by the dominant baseline. Because we aligned all glyphs so that the dominant baseline is 0, we now have that second alignment as an offset onto each glyph. These two adjustments are only applied when aligning glyphs, not when aligning inline boxes to their parents.

Same sequence of glyphs is now translated so that the boxes are aligned to eachother on the hanging baseline. The additional translation is marked in blue and the length of the translation is added underneath the original yellow number, also in blue.

As that is the next step. Here, we have the alignment-baseline align portion. By default, this is the same as dominant baseline, but if it were any other value, it’d use that instead.

The same sequence, but now the baseline shift is added in red at the final set of glyphs. The length is marked as (6-3), to indicate that the total length added is the length of the alphabetic to super script(6), with the alignment baseline (3) removed.

Finally, we have baseline-shift. If baseline-shift is super or sub, we subtract the shift from alphabetic to alignment point caused by alignment baseline from the super or subscript value. If it turns out to be a line-relative value (top/bottom), these will not be processed during the regular alignment process, but instead done after line-breaking.

This approach has some benefits. Firstly, the coordinate system shift makes using the dominant baseline as the point to which align the current text position trivial. The second, which is a little bit more odd, is that when having done that coordinate system shift, the total shift needed for alignment and dominant baseline together will be constant over the whole text as long as alignment baseline defaults to dominant.

This is useful, because later, you will need to know whether vertical align (that’s baseline-shift and alignment-baseline) changes if you want to know whether the strike-through for text-decoration needs to be split into a new chunk. This way, you don’t need to test whether the css property is set, but can instead test whether this offset is changing.

Then, for the current text position problem I had, I subtract the shift caused by dominant+alignment baseline from the first text transform. This means that if an absolute SVG text transform is applied to a text with dominant or alignment baseline applied, it will still move the origin of the glyph to that position.

This is showing Krita’s debug view of the text layout. Red dots are the glyph origin, green are ligature caret offsets and the big white dot at the start is the shape origin. Here, our slogan is set to dominant baseline: hanging, but the first two clusters are super-scripted. If we don’t consider baseline-shift to cause a relative offset, that white origin dot would be at the red origin of those super-scripted clusters, and removing those clusters would make the whole text move upwards.

For baseline-shift however, that offset is stored separately. According to the SVG spec, we first need to apply the relative transform, then text-length, and then the absolute transform. The spec says to take the relative offset, subtract the current character position, and then add the absolute offset to that for the total shift. I’m not 100% sure why it does that, but for my purposes, the baseline-shift is added to the relative offset. This means that an absolute transform will always have the baseline-shift (if any) added in addition, which in turn should make it possible for pieces of SVG text to start with a superscript without moving the whole text position if that superscript is removed.

That means that for me, line-relative values like vertical-align: top don’t necessarily need to be mapped to baseline-shift or alignment-baseline for whether they count as alignment or not, but rather for how they interact with SVGs absolute character transforms. Its kind of why I have decided to not show these options in the UI for now, because I don’t know what makes sense here…

Em box calculation.

Because base table is not that widely used, CSS-inline-3 has a number of fallback calculations, and Harfbuzz implements a number of these. I am somewhat sceptical of the ideographic em-box calculation, however:

Sequences of Latin, Simplified Chinese, Tibetan, Korean and Devangari aligned by the ideographic baseline. Due synthesis of this baseline, the Tibetan is notably offset towards the top.

Here we have script samples in, left to right, Latin, Simplified Chinese, Tibetan, Korean and Devanagari, all aligned by their “ideographic” baseline (pink). All glyphs are from Noto Sans and -Serif fonts. Blue square uses the font-size used by the text for its width and height.

What is going on here is that if we look at the official way to calculate the ideographic em box, we end up with a rectangle that uses the em size for the width, and the sTypographic ascender+descender for the height… but only if the font is evidently a CJK font.

Noto Serif Tibetan is not a CJK font, and has a large descender to reserve enough room for the clusters that Tibetan produces. That means that the ideographic bottom is far lower than anyone would expect it to be.

So I decided to adjust the em-box calculation a little. For vertical, upright, glyphs, it defaults to the em-size in width if there’s no Base table ideographic values.

For horizontal, it uses sTypo ascender + descender when the font either has a CJK script in its design scripts metadata, or, more likely, has ‘水’ (U+6C34) as a character, which I needed to test anyway as it is used for the ic font-relative value.

If neither is the case, then it scales the ascender as per CSS-inline-3 em-over. Then compares that to, first, the hanging baseline (if present), or otherwise the cap-height. If one of these is larger than the scaled ascender, it will use that as the em-box top. Then, the em-box bottom is 1 em below the em-box top.

Mixture of Han ideographs, Latin and Tibetan text, using different dominant baseline alignments, using the the described method for deriving the non-CJK embox. The CJK font is Noto/Source Han Sans HK, which has ideographic values defined in the font, others are Noto fonts without these metrics.

This is by no means perfect, a bit messy honestly, but it does look a lot less like something broke.

Before I forget, there’s another oddity with the baseline properties, and that is that it only allows alignment to ideographic-bottom and central, but not ideographic-top. Arguably, because of the way the em-box is calculated, the ideographic top can be the same as ‘text-top’, but if you look at non-CJK fonts, that isn’t necessarily the case. Similarly, character face metrics are not defined at all. I guess part of them problem is figuring out what exactly is being used in real life and for what purposes, but it is a little odd.

Text decoration

When I did a talk at the last Libre Graphics Meeting, one of the questions afterwards was “so, how are you calculating the text-decorations, are you using css-text-decor-4?” The answer was “no”, largely because I only realized that had a different way of calculating after I’d finished my initial text layout work.

However, after reworking the baseline alignment stuff, text decoration also needed overhauling, and I figured to take into account decor-4’s new specifications would make sense.

The biggest difference between decor-3 and decor-4 is that for decor-4, the strike-through is split when the font-size changes in without vertical-align changes.

Beyond that, decor-4 suggests that the underline position, when not using alphabetic underline, should use the em-box lower edge… Which I don’t quite understand, as the em-box is never defined anywhere in decor-4, and if it is the em-box meant by the opentype specs, then that is going to be too high up for a Tibetan fonts that does have ideographic edges defined.. So I am ignoring mentioned of ‘em-box’ in decor-4, and assuming it means the ascent+descent instead.

So to tackle these differences, I first calculate the ranges the text-decor needs to be applied to. To keep consistency, decorating boxes need to be derived, and a span of decorated text can have multiple decoration boxes in case there’s a line break for whatever reason. Because I am working in SVG, I’ve, in the past, also decided that it would make most sense if the decorating boxes got split whenever there’s an anchored chunk, as these, within svg, are caused by both line starts as well as absolute positioning changes.

So what I end up doing is that every time an anchored chunk start is encountered, a new decorating box is created for that range. Then, said range is “trimmed” to remove the hidden/collapsed/unaddressable/white spaces from the start and end of the range, as per §2 of the spec:

Underlines, overlines, and line-throughs are drawn only for non-replaced inline boxes, and are drawn across all text (including white space, letter spacing, and word spacing) except spacing (white space, letter spacing, and word spacing) at the beginning and end of a line.

After getting the ranges, the decorating boxes proper are calculated by going over each range. For under and overlines, the layout bounds (a box of the advance and ascent+descent in line-height direction) are used to create the decorating box. When dealing with “auto” or “alphabetic” positioned underlines, the layout box instead uses the ascent+offset to alphabetic baseline as the line-height. Because back in the dominant baseline section, all metrics got adjusted to the dominant baseline, you don’t need to know what the dominant baseline is, just how much distance there is between the alphabetic and origin.

For line-through, the start of each range creates a new line-through, but also whenever the font-size changes (but not when the font-size and total baseline offset change). Because of the way I calculate the baseline alignment, the total offset only changes when alignment baseline is doing something else than dominant baseline, or the baseline-shift changes, both of which are what constitute a vertical-align shift. By checking the value difference, we don’t need to know whether vertical-align is actually set in the CSS.

EDIT: I might change this again in the future, as I just realized that if you change the dominant baseline halfway in a decorated span, the alignment baseline will ensure it stays in the same place, but it does change the offset, meaning the line-through changes. Maybe I am entirely overthinking this and the decor-4 spec writer was thinking “avoid super-script problems” without any regard for inline-3.

In the above image: Two samples originally from the xsl 1.1 spec. The top uses dominant-baseline to align all glyphs to the hanging baseline, while the bottom sample uses alignment baseline on the last piece of text to align it to the hanging baseline. Because the latter changed the vertical-align, it does not split the line-through.

The spec also states:

To prevent superscripts and subscripts from throwing this [underline offset] position off-kilter, an inline with a non-initial computed vertical-align is treated as having the ideal underline position of its parent.

I’ll freely admit I’m just not counting the metrics for these offset sections, and only making the decoration box/line through larger, because that prevents me from having to know what the ideal value of the parent is, simplifying the algorithm. In the end, the underline offset, line-through offset, and line thicknesses are averaged by the amount of samples I have, which does not include these ignored sections.

Cursor stuff

The last bit is the slanted cursor line metrics. This is mostly idle musing, as the metrics involved themselves are rather trivial.

When you have a piece of text that is slanted, like in an italics face, this slant might be stored inside the font metrics, so that the cursor, or caret, can be slanted as well.

Because slant ratios are floating point, and the OpenType metrics (from hhea and vhea respectively) are integers, the value is stored as a rational number derived from a caret “run” and “rise”. These by default are 0 and 1 for horizontal, and 1 and 0 for vertical. Then, there’s an offset, because if you slant a line as long as the line height, that line will not go through the origin of the glyph anymore. This extra offset corrects that.

I had initially added this to the font metrics in a thought of avoiding extra calculations when retrieving glyph data, but when I started to move it I came to the conclusion I was checking the font anyhow to get the ligature caret offsets.

So instead some musings:

In particular, most of this blog was about baseline-alignment, and offsets in the line-height direction (called box-direction by the CSS specs). If a glyph is slanted somehow, shouldn’t that be taken into account here? Like, there isn’t actually a self-evident answer here:

When slanting an italic, some fonts have it so that the slanted glyphs are fully encompassed by the advance, and then use kerning to reduce the advance to what is strictly necessary. Because kerning doesn’t happen across itemization runs, and having a different slant counts as a different run, this means that there’s a little space before and after slanted glyphs, so they don’t knock into non-slanted glyphs.

But not all fonts do that. Nor do all fonts have slant carets despite being visually slanted, or have extra x and y data to allow shifting the super and subscripts slanted (I’m making Krita use this data right now, because “why not?”, but it is not require by the spec).

And while we’re at it, if a script is being synthetically slanted… should that happen across its dominant baseline origin?

Maybe the spacing issue, at the very least could be resolved with having a “spaces before/after italics” for the new auto spacing properties in css-text-4, but that in itself will not provide answers for how to handle baseline offsets.

Final thoughts

When I had finished font file handling, I wanted to do something slightly less heavy, and I thus chose to work on OpenType features. This was because I could already tell that fine-tuning the handling of font metrics was going to be quite a big task, and I was correct.

Reworking baseline alignment to shift from XSL/SVG 1.1 to CSS-inline-3 in itself was 4 days for reading the specs, and then another 4 days for untangling all the whole system. Text decorations too required multiple days to get right, to the point where, now I am writing these final thoughts, I am still building some changes to get past an edge case.

For the baseline alignment stuff, I was somewhat… Confused? Disappointed? In the way how people talked about the feature, saying things like “Alignment of glyphs of different font-sizes is not an important feature”, while any understanding of the scripts involved makes it clear it must look mighty strange to a native user. Meanwhile, Latin text has support for ligatures and hyphenation and the like, which I can’t say is any more or less important than proper baseline alignment.

That said, the feature itself is weirdly… uninteresting once it is implemented. Because the metrics largely align to the parent baseline, it is only really interesting when you start nesting text, meaning that in most cases you only really want to set the dominant-baseline. Because alignment-baseline takes over dominant-baseline of the parent, I am having a hard time imagining situations where you’d set it by itself. I’ve had situations where it seemed like it wasn’t working, until I realized that I was comparing the glyphs in question to the line they were in, and not the top-level font metrics.

This is one place where I’d like to have some better UI so this becomes a little less abstract. I don’t regret implementing the feature, in any case: Because for Krita the text tool’s current main goal is to be able to type set comics, we need to ensure it can be used in situations where otherwise people would resort to calligraphy/hand lettering. Proper baseline alignment, as well as font-size differences seem to me like they’d be common in that kind of type setting.

Other than that, these properties are peculiar in that many of them don’t inherit: Baseline-shift doesn’t inherit, nor does alignment-baseline or the main text-decoration properties. This is because they can nest: You can have a baseline-shift inside a baseline-shift, a text-decoration that paints over another. Beyond these properties, there’s one other important SVG text related CSS property that also nests: Unicode-bidi, meant to assist in managing the handling of bidirectional text layout.

Because the majority of WYSIWYG text tools don’t support editing text-layout as a tree of nested items, there is no common way to handle this. Which means I probably spend way too much time on getting this text layout to handle stuff most artists won’t ever see, which is a little unsatisfactory. I think it’ll remain that way for a while though, until I can figure out a way of manipulating these nestable properties that doesn’t result into extensive tree management.

My next text layout blog will be about some remaining line breaking related stuff, mostly because I am already done with the work for that…

Appendix

Pixels versus Points

For those not aware, there is a difference between pixels and points. In particular, pixels are the amount of samples on your screen, while points refers to “PostScript points” and are 1/72th of an Inch, a rounding of actual typographic American point, which is used by TeX as 1/72.27. There’s a ton of variations on typographic points, but for the purposes of computer graphics, we largely use the 1/72 PostScript style points whenever we talk about Points.

Now, to convert between pixels and points, you then need to know how many pixels should go in an inch. This is what we refer to as PPI or DPI, pixels per inch or dots per inch respectively.

Now, according to the wikipedia article about typographic points, the original idea behind a point was to measure the stem-width of a glyph. So it makes sense that when choosing a standard PPI for their displays, Apple decided to use 72, as that would mean a glyph’s stem would be big enough to be covered by a single pixel. Following that, at Microsoft, folks thought that was too small, a screen would be much farther away than a piece of paper on a desk. So they went with 96 dpi instead. Meaning that the pixel size is 4/3rd that of the point size.

At one point, the W3C decided to make that the official ppi for the internet, by including it in the CSS specs. There was an attempt to change this in the 2000nds when the first hidpi screens were available on smart phones, but apparantly using actual DPI didn’t work out here, and it was much easier to allow for a pixel scaling factor to have x amount of design pixels map to x amount of device pixels. Which is annoying, but workable.

Except Apple doesn’t follow the CSS spec. On Apple’s Safari web browser, pixels are 72 ppi, meaning they’re the same as points. But points are not PostScript points either. In fact, if you look at Apple’s developer docs, it seems they use points to talk about design pixels.

That means that web development wise, all the absolute CSS values besides pixels are completely pointless (pun not intended, but, ‘eh’), because you have no idea what the viewer is going to see at all.

This also explains why Apple-first software like the Adobe products seem to confuse points and pixels (like, PSDs storing text in points while it clearly meant to use pixels), even though you’d think this is the one place where that would not happen. I even recall a post by an Adobe engineer on their forums lamenting that they tried for years to get their user base to understand what DPI is and failing. And honestly, if those designers were all on Apple products, yeah, I’m not surprised.

SVG defaults

So, CSS typically is supposed to be applied to a whole document, and also cascade over that whole document. Unfortunately, while I can make CSS cascade over a whole text-object, Krita is not set up to handle cascading over a whole document. This means we right now have two problems:

  1. What is the default font-size? If whole document cascading were possible, you’d set the font size on the document css properties. However, it is not, so right now we just default to 12 pt. The best I can do here, probably, is to always save root{font-size:12;} in all Krita svg files so that this is resolved upon loading.
  2. Setting a whole text-object to baseline-shift:super makes no coherent sense without a root level font that cascades in. However, arguably just setting baseline-shift:super on a whole text object makes no sense in SVG overall, as unlike HTML, absolute positioning is the normal way to use SVG objects. There’s no coherent sense of what baseline-shift even does in this context, and I don’t really know how I am supposed to treat it due that.

Ascender and descender

Gonna ramble on about ascender and descender a bit more:

I already mentioned that ascender and descender don’t really mean much. Frequently, ascender+descender is much bigger than the em-size.

In the above image, all these fonts are at the same font-size, but vary in visual size and ascender/descender. The shareware font Papaya Sunrise is most egregious here, for the others, it seems that most fonts have the cap-height at the same height. The font marked “Personal” is mine, showing its titling caps. I know there the ascender+descender is the exact same as the em-size, which means that all the other fonts have slightly larger values…

Now, traditionally, the ascender is the metric corresponding to the thing that sticks out on bhk, which are often higher than the size of the capitals, and descender is the lower part of gj and q. Arguably that is also where the digital fonts’ ascender and descender should be, especially now that we don’t use bitmap-based fonts anymore, and don’t need to confuse the ascender and descender with the maximum bitmap extends.

However, most Latin using languages make heavy use of diacritics, many of which hang above or below the main glyph. English is one of the few that doesn´t really need them, and unfortunately, it seems a lot of text layout styling is done by english speakers. In addition, not all software anticipates that the line-height needs to be a little bigger for those diacritics (think terminal emulators). so if you want to guarantee that diacritics will be automatically fitted, it is better to add a little bit extra ascender and descender to ensure that the diacritics will not overflow into the next or previous line.

Sample SVG

Throughout this blog I’ve been showing samples of text with baselines. I made these by taking the writing system samples in QFontDatabase, and then generating SVGs for those in the script specific Noto fonts, at size 12, and aligning some metrics lines to the various metrics retrieved from Harfbuzz (with fallbacks that Harfbuzz calculates based on CSS-inline-3). Then in inkscape I made all those texts into paths, turned everything into svg symbols, and finally I handedited them so all metrics are colored with CSS classes.

The QFontDatabase samples aren’t the greatest as showcasing the different scripts’ typographic qualities (Latin shows AaÃáZz, which, sure, shows diacritics, but AaÃábg would’ve shown the descenders and ascenders, and Devanagari and Tibetan would’ve both benefited from their conjuncts and clusters), but I can only read Latin, so this was better than trying to desperately come up with something myself. Script samples are a bit of a problem UI wise, and sometimes I wished there was a good repository for them. Technically fonts themselves have the ability to store a “preview”, but this is hardly ever used.

Anyway, I’m telling you all this, because I’ve uploaded the SVG file so that people can play with it.

Thursday, 24 April 2025

Integrate QML Window's Background with the System's Color Palette

As a person who enjoys responsive designs, I like to resize apps and see how quickly the UI updates after the resize. If you're making an app with Qt Quick, you'll sometimes find that, as the window size increases, the UI will lag behind for one or more frames, revealing a white background on two of the window's edges, that usually looks out of place on desktop applications.

Blog_Resizing_Javier_Thumbnail-For-a-Correct-Background-Color-When-Resizing-1

Thumbnail: A window with a white border where there should be a background color.

Why is the color background revealed when the window size is increased?

This issue stems from the fact that Qt Quick windows will render a background color, white by default, before the position of elements is updated by the scene graph, which lags at least one frame behind the resize because it uses a frame buffer. If the default background is white, that means the QML window itself is not aware of your application's theme and that is intentional; after all, only desktop apps need to be aware of the background color used by native applications.

Qt Quick Controls, however, are theme aware, and that may include your platform's theme. This is why, when you make an app for Windows, if you set a Label against the application's default white background, it will look just fine. But when that app is moved to macOS or a Linux distribution with a dark theme, the label's text will be drawn with a light color to match that theme, and that will make it nearly impossible to read against the default white background.

One solution for that issue is to place these components against a control that extends through a large area, such as a Pane. Qt Quick Controls' theme awareness will ensure the right background color is used. However, controls are only drawn within the window, so when the window is resized, its true background color will seep through -- and that's the issue with which we're concerned. The correct solution is to set the window's background to the system's or the theme's background color, which will make the frame buffer's lag far less noticeable during resizing.

How to Make Qt Quick Window Backgrounds Aware of Your System's Theme

Setting a background color is as simple as assigning a value to the "color" property of the window, like so:

Window {
    color: "lightblue"
}

To have it match the background color of native apps, use QML's SystemPallete type, as follows:

import QtQuick
Window {
    color: systemPalette.window
    SystemPalette {
        id: systemPalette
    }
}

Here you can find the various default styles used by Qt Quick for each supported platform. By making use of SystemPalette, the appropriate background color for the current platform will be chosen.

It is important to note that, using SystemPalette, the background color will be chosen based on the current platform and not on which QML style has actually been applied, which may differ if your app makes use of a fixed style for all platforms.

Blog_Resizing_Javier_1-ezgif.com-optimize

Animation of a window resizing, letting the white background seep through a corner.

Blog_Resizing_Javier_2-ezgif.com-optimize

Animation of a window resizing. The background color is consistent with the theme at all times.

Platform Style vs. Fixed Style

As an alternative to following the platform's theme, you could provide a uniform experience for all platforms by enforcing one style for all. There are minor performance advantages to having a fixed style for all systems, but these only tend to matter on low-end embedded hardware, where every cycle counts. There you could use Compile-Time Style Selection, so the QML compiler knows which style is in use and can generate C++ code for bindings.

If you are creating a desktop application, your theming choice should be based, not on performance, but on the level of system integration and the user experience you wish to provide. A photography application might benefit from having a dark mode at all times so the picture colors can pop. An e-mail client, on the other hand, might do better integrating with the system's theme or allowing users to choose between a light theme and a dark theme. Light backgrounds generally provide better contrast, which makes them a good default choice for emails. However, users may prefer the system theme or a dark theme, if it's easier on their eyes.

Blog_Resizing_Javier_Screenshot_20250110_123920

A window from one OS, with a light theme. Its contents can be read just fine.

Blog_Resizing_Javier_Screenshot_20250110_123546

A window from a different OS, with a dark theme. Some of it contents cannot be read due to a lack of contrast.

Crafting Seamless and Theme-Aware Qt Quick Applications

By thoughtfully managing your Qt Quick application's window background color, you can significantly improve its responsiveness and aesthetic integration with the host platform. Whether you opt to align with the system's theme using SystemPalette or enforce a fixed style for uniformity, your choice should reflect the needs of your application and its users. Balancing system integration, performance, and user experience ensures your app not only looks great across platforms but also feels natural to use, providing a polished and professional result. With these tools and insights, you can create dynamic, theme-aware applications that stand out in usability and design.

The post Integrate QML Window's Background with the System's Color Palette appeared first on KDAB.

The Skrooge Team announces the release 25.4.0 version of its popular Personal Finances Manager based on KDE Frameworks.

This second version is still for Kf5/Qt5 and Kf6/Qt6.

Changelog

  • Correction bug 498626: Filter on specific attribute doesn't work
  • Correction bug 498903: Import fails for numbers with more than 3 decimal places
  • Correction bug 498613: Application-wide Currency Formatting Issue in Multiple Distros and Package Versions
  • Correction bug 501642: Segmentation fault on quit
  • Correction bug 501993: Segfault when started in a path containing json files
  • Correction: Better icons
  • Correction: Better tooltip for tabs
  • Correction: Better performances on balances computations

Wednesday, 23 April 2025

It’s been a bit over a week since the last foss-north conference. This year it really felt like the energy has returned after Covid. We’re back to double tracks and we had almost 200 attendees.

For those of you how don’t know, foss-north is an annual conference held every spring in Gothenburg, Sweden. It is split into a community day, and then two conference days.

The first conference was held back in 2016, to fill the void when fscons changed format. Given that we did two conferences back in 2020 (covid did not go away over the summer…) this was the 11th conference and the 10th year running. This was also the first conference run by the amazing Tobias. It feels good to see the event in his capable hands. He brings a lot of new energy into the organization!

Over the days there were lots of talks – and even a workshop. Carol Chen braved the demo ghosts and ran a hands-on session.

Also, we had cake!

A big thank you goes out to all the speakers, visitors and sponsors. You made this possible! I hope to see you all next year. Tobias has already booked the venue!

Monday, 21 April 2025

This week Plasma developers have descended on the lovely Austrian city of Graz! 🍦

During the 2025 Plasma Sprint in Graz we’ll be discussing a range of topic both for desktop and mobile. Of course there’ll also be a fair amount of hacking. If you are in the area, feel free to stop by at Grazer Linuxtage on Saturday April 26th where we will have a booth!

Hacking aggressively

Sunday, 20 April 2025

I love the Kate Text editor. I use it for pretty much all the programming projects I do. Kate has been around for long time now, about 20 years! At least earliest blog post for it I could find was written in 2004.

I wanted to go over my workflow with it, why I like it so much and hopefully get more people to try it out.

My Kate setup

How I set up my editor

Here's some settings I find important for my workflow. There's tons more settings to fiddle with, but most of the defaults suffice for me very well.

Plugins I use

Here's a list of the plugins I have enabled. Some of them are self-explanatory, some I will go more in detail later.

  • Build & Run
  • Color Picker
  • Colored Brackets
  • Document Preview
  • Docment Switcher
  • External Tools
  • Formatting
  • Git Blame
  • Kate Debugger
  • LSP Client
  • Open Link
  • Project Plugin
  • Search & Replace
  • Snippets
  • Terminal

Move and split views

In Kate, most views are in their own little tabs. You can right click these tabs and select "Own section" which splits them into their own areas. In the screenshot I provided at start, you can see my Projects and Git view being split like this, so they can both be open at the same time.

Context menu for splitting panels

Do note that these are saved per session, so sessions can have different splits.

Language server

In LSP Client settings, you can toggle multiple things I won't go into detail in here. What I do want to mention is the User Server Settings file, which can be edited from this menu. To override some defaults, you can just copy the default setting from the default tab, put it in your own settings tab and then just change the values.

Here's my LSP settings: dotfiles/kate/lspclient/settings.json

Debug adapters

Debugger settings work similarly to LSP setting, however you may have to restart Kate to recognize any new changes.

Here's my DAP settings: dotfiles/kate/debugger/dap.json

Formatters

For code formatting, there's also it's own setting menu, however as of writing this only the languages mentioned in default settings are supported.

If you want to tell formatter to stop autoformatting, you can set it like this, using clang-formatter as example:

{
  "clang-format": {
    "command": [
      "clang-format"
    ],
    "formatModifiedLinesOnly": true,
    "formatOnSave": false
  }
}

Here's my formatting settings: dotfiles/kate/formatting/settings.json

Shortcuts

Shortcuts are very personal thing and I encourage learning the defaults if at all possible. I have changed mine around a bit, for example Ctrl+Shift+Space for Quick Open and Ctrl+Space for Find action. More of these two later!

Path setting

Kate can't always find language servers for example, if it can't resolve your path. Use this path setting to load binaries from paths like ~/.local/bin. You can find this under Behavior settings at least on Kate 25.04.

Sessions

Sessions are basically group of projects. Do not treat sessions as projects themselves, that will only cause sadness.

I have separated my Work and Personal projects in their own sessions. This means that during work day, I can right click the Kate icon, select Work session, and then Kate will always open for me in that session until I choose Personal session.

In session settings I have set Kate to always open the last used session because of this.

I highly recommend this way of working. You could also have sessions for differently themed projects: Gamedev, web, etc..

Just make sure to create at least one session when starting out, then you can use that as a template for the other sessions in Kate session manager.

When I used to write more notes with Kate, I had it's own Notes session with both my Notes folder as a project folder and my blog.

Colorschemes

Kate uses KColorScheme library for it's colorscheme needs. You can very easily to copy a ready colorscheme and then start modifying your own changes on top of it.

You can also create a completely new colorscheme if you wish, like I have done here: Revontuli/Kate

The colorscheme files in the library are their own XML format which is not ideal IMO but it gets the job done. It would be awesome if Kate supported tree-sitter but so far I haven't heard anyone trying to implement that.

File quick switching

I mentioned this earlier in shortcuts section. If you're like me and have multiple projects open, you can use the Quick switcher and start typing the filename you need to open.

Quickswitcher being used

If you just want to open a specific project, you can type project bla and it will find that project for you from your project list.

This is why I have it bound to Ctrl+Shift+Space, so I can just press it and quickly find the file I need.

This is more interesting: Instead of searching for files, you can search for any action Kate has.

For example, if you want to look for terminal related actions, you can type "Terminal" and it lists everything plus any potential shortcuts.

Action search being used

Fun fact, you can use this in other KDE apps too, like Dolphin or Konsole! The default key combination for it is ctrl+alt+i but I use it so frequently I set it to ctrl+space.

Build and Run

The name of this plugin is a bit misleading IMO because it can do so much more than just build.

Build and run view

In essence, it's a command runner. You can give the command a name, then build command and run command.

For example in the above image, my game project has only one command called build which has build command odin build ./src -collection:src=src -debug -out:./build/ArtificialRage and run command odin run ./src -collection:src=src -debug -out:./build/ArtificialRage.

I can then just use the action search to build and run my game project.

For your projects, you can then add file called .kateproject.build in your project root folder, so Kate will pick that up. They're just JSON files that look like this:

{
    "target_sets": [
        {
            "cmake_config": "",
            "directory": "/home/akseli/Repositories/artificial-rage/",
            "loaded_via_cmake": false,
            "name": "artificial-rage",
            "targets": [
                {
                    "build_cmd": "odin build ./src -collection:src=src -debug -out:./build/ArtificialRage",
                    "name": "build",
                    "run_cmd": "odin run ./src -collection:src=src -debug -out:./build/ArtificialRage"
                }
            ]
        }
    ]
}

The .kateproject files can store much more information about the project, but they are not very user facing yet sadly. Something on my eternal to-do list.

Also when running the commands, there's "Output" tab that shows the results. It's basically view to your terminal, but if you encounter an error or warning, and the output panel can parse the file and location from it, you can click that item and it will open code at that position where the error/warning appeared.

Language server

As I mentioned above in the settings, Kate does have support for Language Server Protocol, as long as they're found from the path Kate looks for them and are configured properly. It's very much the same support one would expect in any modern editor.

One thing I do use a bunch thanks to it is looking for references to specific symbol, searching symbols across all my open projects and symbol overview.

Debugger

Kate has support for Debug Adapter Protocol, which is the lesser known cousin of Language Server Protocol.

This allows you to debug applications visually with breakpoints, check the stacktrace by using Locals and stack panel, and so on.

As long as the configuration is correct, debug adapter is found in path, and the executable, working directory, etc are given to Kate correctly, it can debug things rather well. The debugger plugin has undergone bunch of polish lately so it's rather good for my use, especially with my game projects.

Debugging my game

For bigger things like debugging whole Plasma desktop, I still use gdb in terminal.

Terminal

Speaking of terminal, the terminal is just Konsole running inside it's own view. The terminal can be made to follow the folder of the file you're editing, and it will share the settings with Konsole. You can also split the terminal from Kate if you need more views. Terminal tabs also work.

So it's essentially just Konsole embedded in Kate.

Git integration

The git integration in Kate is simple-but-works. You can diff, stage and discard lines/hunks directly from the editor (I use this a lot).

Git diffing

Then of course, you can stage and unstage, commit, push, pull etc. directly from Kate. As of writing this, you can't rebase yet, but for more complex tasks I tend to often use something like lazygit, which I highly recommend.

Git staging

All of this has inline git blame, so you can see per line who did what and where. This is very handy in projects with multiple devs.

Snippets

Kate has a snippet plugin that allows one to create simple or super complex snippets, for example for boilerplate.

Snippets tool

Here's example for my blog header snippet (don't mind the bad JS I wrote it very tired)

The snippet text, that will be pasted into a markdown file:

+++
title = "${title}"
date = ${justDate()}
aliases = ["/${dateUrl()}/${lowerFilename()}.html"]
[taxonomies]
tags = ["${tag}",]
+++

My ugly javascript mess for creating dynamic items:

function fileName() { return document.fileName(); }
function fileUrl() { return document.url(); }
function encoding() { return document.encoding(); }
function selection() { return view.selectedText(); }
function year() { return new Date().getFullYear(); }
function upper(x) { return x.toUpperCase(); }
function lower(x) { return x.toLowerCase(); }
function today() { 
    var date = new Date();
    return formatDate(date);
}
    
function padTo2Digits(num) {
  return num.toString().padStart(2, '0');
}

function getDateName(date){
    var names = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
    return names[date.getDay()];
}

// 2024-06-13 Thu 10:00
function formatDate(date) {
    var dateString =     [
      date.getFullYear(),
      padTo2Digits(date.getMonth() + 1),
      padTo2Digits(date.getDate()),
    ].join('-');
    
    var dayName = getDateName(date);
    
    var time =     [
      padTo2Digits(date.getHours()),
      padTo2Digits(date.getMinutes())
    ].join(':');
    
    var timeString = dateString + " " + dayName + " " + time;
  return timeString;
}

function justDate() {
    var date = new Date();
    var dateString =     [
      date.getFullYear(),
      padTo2Digits(date.getMonth() + 1),
      padTo2Digits(date.getDate()),
    ].join('-');
    return dateString;
}

function dateUrl() {
    var date = new Date();
    var dateString =     [
      date.getFullYear(),
      padTo2Digits(date.getMonth() + 1),
      padTo2Digits(date.getDate()),
    ].join('/');
    return dateString;
}

function lowerFilename() {
	return lower(fileName());
}

So yes, you can write Javascript to generate snippets for you, like here I've done with date and time, filename, etc.. It's super useful.

You can then use code-completion shortcut and type snippet-namespace:snippet-name and press enter, and it will run the snippet for you. You can make the snippets only appear for files in specific language.

It's a very very powerful tool for generating simple boilerplate code. Requires some upfront work but after that's done, it works wonders.

Here's also some of my markdown snippets: dotfiles/kate/Markdown Snippets.xml

But why do I love it so

Kate has basically all the features I need, out of the box, without having to download random extensions or plugins. Sometimes I just have to enable plugins, maybe write and change some configurations.. But when that's done, it's all just there and I can start using it.

I tend to back up my configs to my dotfiles git, so I can just in subsequent installs use them immediately.

Hang on, VSCode has all of this and more!

You're right, VSCode has a lot of these things too. However with VSCode I've noticed that you have to sometimes download whole language as an extension. Then, the extensions may clash with each other, especially if you have two extensions that do different things for the same thing: I remember having two CMake extensions where both had something I needed, but they both also overlap in some basic features, so it got very confusing.

That's probably all skill-issue of course. But I like that I don't have to download any extensions or plugins or whatever.. I can just import my settings and start working.

VSCode being electron and Microsoft doesn't really help my feelings either, especially since some of their extensions like C# and Python are proprietary, and they're working on more extensions to be like that:

So yeah, I rather use something like Kate that I know will be around for long time and won't turn into proprietary mush, nor try to tell me to use their newest "AI" slop tools.

Kate is very much the embodiment of KDE tagline: Simple by Default, Powerful when Needed. It does all I need and allows me to modify it as I need.

I've contributed to Kate bunch of times, and every time it has been nice. The maintainers of Kate are very lovely patient people who deal with my nonsense. :)

And that's why I love it so much: The people around it, the out of the box experience, the simplicity yet powerfulness..! ❤️

Give Kate a try today! :)

If you have any issues with it, report them on bugs.kde.org. You can also send wishlist items there. There is also wishlist of things on KDE Discuss, feel free to chat there about it or ask for help in general.

PS. For the modal text editor lovers, Kate does have Vi mode. I use it from time to time, but I prefer Kate as it is to be honest. For my terminal/modal editing needs I use Helix editor, but most of the time I'm using Kate.

Friday, 18 April 2025

This release includes new features and fixes for AdBlock, VerticalTabs, stability fixes, more usability features and translation updates.

General changes

  • Add input method hints to main input fields (url, search bar, etc.).
    • This allows on-screen keyboards to show more appropriate symbols or disable the auto correct feature.
  • Show the URL of bookmark and history items in statusbar when hovered over in the menu or sidebar (BUG: 448238)
  • Open all URLs in command line arguments (BUG: 413195), (Original author: abogical (Abdel-Rahman Abdel-Rahman))
  • Add “Preffer color scheme” option (BUG: 439891), (BUG: 497331)
  • Add option to add site permissions in preferences (BUG: 495391)
  • VerticalTabs: Override Alt+NUMBER keyboard shortcut
  • StatusBarIcon-Network: Restore online status detection
  • KDEIntegration: Fix KIO scheme handler (BUG: 491247)
  • Add option to block automatic popups
  • Qt 6.9 compatibility fixes

AdBlock

  • Add support for “rewrite” filter
  • Add support for remove rule with simple CSS selector
  • Ignore inactive hiding rules
  • Add support for “websocket” option
  • Rewrite Adblock Dialog (fixes crashes)
AdBlock dialog, Left: old version, Right: New version

Changelog

  • PyFalkon: port away from deprecated API (by Ilya K)
  • PyFalkon: Disable broken tests (by Juraj Oravec)
  • Update CHANGELOG for 25.04.0 release (by Juraj Oravec)
  • Fix crash when loading folder in bookmarks toolbar (by Juraj Oravec)
  • CI Flatpak - Add desktop notifications permission (by Justin Zobel)
  • CI Flatpak - Add kwallet6 dbus permission (by Justin Zobel)
  • CI Flatpak - Sort finish arguments (by Justin Zobel)
  • CI Flatpak - Update flatpak runtime (by Justin Zobel)
  • snapcraft: Fix crash by disabling webengine sandbox, we are already a (by Scarlett Moore)
  • CI: Add linux-qt6-next build (by Albert Astals Cid)
  • Add input method hints to input fields (by Juraj Oravec)
  • Select newly opened tabs from links and scripts (by Juraj Oravec)
  • Fix SiteSettings autotest (by Juraj Oravec)
  • Adblock: Add support for “rewrite” filter (by Juraj Oravec)
  • AdblockDialog: Add model for the tree view (by Juraj Oravec)
  • Adblock: Add support for remove rule (by Juraj Oravec)
  • Show history & bookmark url in menu on mouse hover (by Juraj Oravec)
  • Block automatic popups (by Juraj Oravec)
  • Use global settings for WebRTC on internal pages (by Juraj Oravec)
  • VerticalTabs Override Alt+NUMBER keyboard shortcut (by Juraj Oravec)
  • AdBlock: Ignore inactive hiding rules (by Juraj Oravec)
  • Adblock: Add support for “websocket” option (by Juraj Oravec)
  • AdblockDialog: Apply filter when tab changes (by Juraj Oravec)
  • SiteSettingsDialog: Add context description for “Set” button (by Juraj Oravec)
  • SBI-NetworkIcon - Restore online status detection (by Juraj Oravec)
  • Open all URLs in command line arguments (by Juraj Oravec)
  • Remove obsolete XSS Auditing option, has no effect (by Juraj Oravec)
  • CookieManager: Fix crash when removing while/black list items (by Juraj Oravec)
  • PyFalkon: Add missing Qz::ColorScheme enum type (by Juraj Oravec)
  • Add “Preffer color scheme” option (by Juraj Oravec)
  • Strip incorrect color iCCP profile from png images (by Juraj Oravec)
  • Preferences: Fix editing per site user agent (by Juraj Oravec)
  • Use angle backend for GPU acceleration on Qt 6.9+ (by Juraj Oravec)
  • Add missing default SiteSettings for intenal pages (by Juraj Oravec)
  • Add option to add site permissions in preferences (by Juraj Oravec)
  • PyFalkon: Remove warning about QCborStreamReader (by Juraj Oravec)
  • Update Changelog (by Juraj Oravec)
  • Port away from QLocale:Country related code (by Juraj Oravec)
  • B KIO scheme handler (by Juraj Oravec)
  • Add missing settings.endGroup() when needed (by Juraj Oravec)

Thursday, 17 April 2025

I had an amazing opportunity to attend Conf KDE India this year. It was hosted in the wonderful city of Gandhinagar, Gujarat known for its well planned modern architecture and Khadi exports. I was really looking forward to see some familiar faces from last year’s cki and meet new ones.

Day 0

Amehedabad view at night captured on my return journey

I arrived a day before the conference in Gandhinagar. I really appreciate Bhushan for picking all of us from the airport, that was a very warm gesture. I had an early morning flight from Delhi so by the time I reached my hotel room I was really tired and dozed off for quite a while. Thankfully I woke up by evening, around the time when few of us decided to meet up for dinner. I also met Joseph for the first time in person, we have been working together on KEcoLab, a KDE Eco project so I was looking forward to meet him in person and suffice to say he is as warm and kind a person irl as he is when remote. After dinner, I worked on my slides and wrote a bit of qml code for my workshop. Rishi arrived later that day or rather early morning next day and we spent a bit of time tinkering with his Steamdeck before we dozzed off.

Day 1

Konqi made with cubes by the local volunteers

The first day of Cki 25 started with Bhushan introducing what KDE Community is, what we do and why we do to the crowd which comprised mostly of eager local college students. This was followed by Vishal talking about “Embracing FOSS in Edtech” a pretty interesting talk about the widespread use of open source software in various Indian schools and state governments.

After the lunch break, Shubham talked about how he self hosts everything using open source softwares and encouraged achieving digital independence through it. It was a pretty interesting talk for me considering I am pretty close to using up my free storage I get with my google account so this was a nice motivating step for me to break away from google. This was followed by a remote talk by Soumyadeep about using GhVmCtl to test GUI applications directly with ci runners. I think this project is quite similar to selenium-webdriver-at-spi that KDE maintains so it was interesting to know gnome is also working on something similar. After the coffee break Ravi talked about using Prav an XMPP based free chat software as an alternative to Quicksy. He covered a lot about the importance of federated ecosystems, data privacy and how Prav is playing a crucial role in this. The last talk of the day was by Subin where he talked about using Malayalam language (a popular language from the State of Kerala wirh roughly 37 million speakers worldwide) on Ubuntu through maintaining the Varnam Project, which is an open source cross platform transliterator for Indian languages. I enjoyed his talk a lot personally because I also speak Malayalam (although I am not a fluent in it) and it was interesting to know the progress he made on making Malayalam language having first party support on linux.

Afterwards for dinner we had punjabi style north indian kulche and lassi, pretty delicious indeed 😋

Day 2

Me conducting workshop on 'Building your first QML Interface'

I started the second day by giving a workshop on “Building your first QML Interface”. The idea was to build a small mvp of Whatsapp web interface (one of the most widely used chat application in India) in qml, although we werent able to completely build it but the feedback I got was that many were excited about qml and actively following along the workshop so I consider that a mild success. After this Shivam gave a talk about Clazy, its architecture, real world usage in KDE ecosystem and technical challenges faced in implementing static analysis. I am ashamed to admit I not used this tool much before but the talk definitely sparked my interest and I am keen to test it out and maybe integrate it into my workflow as well. This was followed by Joseph talking about End of 10 upcycling campaign also highlighting its importance in the Indian context, again another interesting talk.

After the lunch break, I enjoyed Rishi’s talk a lot about using nix and integrating it into your workflow. I have used nix os in the past and my spare laptop still runs it so I was happy to see it being mentioned in a kde conf. After this Keith talked about the widespread use of open source software in Australian schools, the talk had quite the parallels to the widespread use of Linux in the Indian state of Kerala so it piqued my interests again. The last talk of the day was given by Sahil where he shared his experience running various mirrors for KDE, VLC, Libreoffice, Blender and many other open source softwares and how we can also do that, another pretty intriguing talk. I have had to battle with Qt mirrors in the past so to know what all goes behind the scene to host these mirrors made me appreciate it a lot more than before and you never know maybe I might host a few mirrors for Qt some day.

After the conference all the speakers met together for dinner and we had pretty delicious south indian food.

Day 3

The third day was more about Workshops and Unconference sessions. I started the day by giving a workshop on KEcoLab, how anyone can use our infrastructure (or rather KDAB’s) to test their application’s energy consumption and optimise it. This was followed by Advaith conducting a workshop on writing plugins for Suse’s Cockpit, a web administration tool for linux machines. Afterwards we had an unconference session by Joseph on End of 10, it was a pretty engaging session by him and focussed a lot about the campaign’s outreach in India. We had another unconference session on HackMud by Advaith, Hackmud is a text based multiplayer hacking simulator and was proposed by him as an alternative way to learn programming. This was followed by demo with steamdeck and plasma mobile on pinephone, and suffice to say everyone were excited to try out the devices. SuperTux was especially the fan favourite game 🐧

Adalaj Stepwell

After the conference ended, we visited the Adalaj Stepwell, a stepwell located in the nearby small town of Adalaj Gujarat.

Traditional Gujarati Thali

We ended the day by having dinner at one of the traditional gujrati thali place which had unlimited servings, this was my first time having a gujarati thali and I was blown away by its taste, specially the mango purée.

Day n/n

This was the last day of my stay in Gujarat and I ended my trip by visiting the local landmarks with Bhushan. We visited the Sabarmati Ashram, the main residence of Mahatma Gandhi and saw many artifacts from his time. I got a few souvenirs for my family from this place and We visited the Sabarmati Riverfront after this.

Me and Bhushan at the riverfront

Regardless to say I had a blast attending the conference and it will be one of my best memories. Huge thanks to KDE e.V for sponsoring my travel and accomodation for the conference.

Very busy releasetastic week! The versions being the same is a complete coincidence 😆

https://kde.org/announcements/gear/25.04.0

Which can be downloaded here: https://snapcraft.io/publisher/kde !

In addition to all the regular testing I am testing our snaps in a non KDE environment, so far it is not looking good in Xubuntu. We have kernel/glibc crashes on startup for some and for file open for others. I am working on a hopeful fix.

Next week I will have ( I hope ) my final surgery. If you can spare any change to help bring me over the finish line, I will be forever grateful 🙂

Welcome to the March 2025 development and community update.

Development Report

Text Tool Rework Progress

Wolthera is still buried in text work for 5.3.

  • Add Language text property, which is important for font-shaping, line-break, word-break and text-transform. (Change)
  • Better font unit conversion and other small fixes. (Change)
  • Ensure alignment, dominant-baseline and baseline-shift follow CSS-inline-3 and SVG2. (Change)
  • Rework text decorations so they are calculated as per css-text-decor-4. (Change)

Palettes

Several bugs in color palette editing have been fixed in the stable build, including failure to save group and number of rows, crash when adding a group to a palette in a document, and lag when adding a swatch. (bug 461521, bug 476589, bug 476607, bug 478715) (Change, by Mathias Wein)

In the unstable nightly builds, the Python Palette Docker has been re-added, which can manage palettes and export them to .gpl and .svg formats. (Change, by Freya Lupen)

Qt6 Port Progress

Krita 6.0.0-prealpha now supports PyQt6, and all built-in Python plugins have been updated to support it alongside PyQt5. (Change, by Freya Lupen)

User-made plugins will require updating by the author to work on Krita 6. Krita 6 will not be released any time soon, but for plugin authors who want to get a head start, see the change link for porting tips!

Wayland Testing

Krita doesn't yet support the Linux compositor Wayland, but it can now be enabled for testing purposes on the unstable nightly builds by setting the environment variable QT_QPA_PLATFORM=wayland. (Change, by Nicolas Fella)

Community Report

March 2025 Monthly Art Challenge Results

For the "Virtual Plein Air Painting" theme, 19 forum members submitted 26 original artworks. And the winner is… multiple entries by @Elixiah. Black Bear Pass:

Black Bear Pass by @Elixiah

Also check out the other entry, Druid Arch, Colorado.

The April Art Challenge is Open Now

For the April Art Challenge, @Elixiah handed the winner's honor of choosing the theme to second-place @Mythmaker, who handed it to third-place-tie @Katamaheen, who has chosen illustrating "Fairy Tales and Bedtime Stories" as the theme. The optional challenge is to design the artwork as a book cover. See the full brief for more details, and paint a familiar story with a new brush.

Best of Krita-Artists - February/March 2025

Nine images were submitted to the Best of Krita-Artists Nominations thread, which was open from February 14th to March 11th. When the poll closed on March 14th, these five wonderful works made their way onto the Krita-Artists featured artwork banner:

Krozz Defender by @Yaroslavus_Artem

Krozz Defender by @Yaroslavus_Artem

Gagarin Scientific Research Station by @Dima

Gagarin Scientific Research Station by @Dima

The Anger That Breaks from Within by @ryanwc

The Anger That Breaks from Within by @ryanwc

Xavier by @MangooSalade

Xavier by @MangooSalade

2025 by @Montie

2025 by @Montie

Ways to Help Krita

Krita is Free and Open Source Software developed by an international team of sponsored developers and volunteer contributors.

Visit Krita's funding page to see how user donations keep development going, and explore a one-time or monthly contribution. Or check out more ways to Get Involved, from testing, coding, translating, and documentation writing, to just sharing your artwork made with Krita.

Other Notable Changes

Other notable changes in Krita's development builds from Mar. 17 - Apr. 17, 2025, that were not covered by the Development Report.

Stable branch (5.2.10-prealpha):

  • Brush Editor: Don't apply active mirror tool to the brushstroke preview. (bug report) (Change, by Scott Petrovic)
  • ACB Palette: Use title for the palette name. (Change, by Halla Rempt)

Unstable branch (5.3.0-prealpha):

Bug fixes:

  • Tools: Fix tool opacity being reset when switching brushes. (bug report) (Change, by D Kang)
  • Tools: Fix sampling screen color when using multiple screens with different screen scaling. (Change, by killy |0veufOrever)
  • Tools: Keep brush rotation during brush resize action. (Change, by Maciej Jesionowski)
  • Layer Stack: When transforming a filter mask or its parent layer, show the content's bounds instead of the mask's entire canvas bounds. (Change, by Maciej Jesionowski)
  • Animation Export: Fix failure to overwrite existing animation sequence. (bug report) (Change, by Emmet O'Neill)
  • Animation: Make changing animation settings such as framerate and start/end frame undoable, and affect the document modified state. (bug report) (Change, by Emmet O'Neill)
  • Usability: Make bundle activation/deactivation clearer, using a checkbox. (bug report) (Change, by Scott Petrovic)
  • Canvas: Speed up canvas panning with rulers, by reducing the rate of ruler updates. (Change, by Maciej Jesionowski)

Features:

  • Animation/Recorder: Update built-in FFmpeg to version 7.1. (Change, by Dmitry Kazakov)
  • Batch Export Plugin: Add Bilinear filtering option. (Change, by Austin Anderson)

Nightly Builds

Pre-release versions of Krita are built every day for testing new changes.

Get the latest bugfixes in Stable "Krita Plus" (5.2.10-prealpha): Linux - Windows - macOS (unsigned) - Android arm64-v8a - Android arm32-v7a - Android x86_64

Or test out the latest Experimental features in "Krita Next" (5.3.0-prealpha). Feedback and bug reports are appreciated!: Linux - Windows - macOS (unsigned) - Android arm64-v8a - Android arm32-v7a - Android x86_64