Skip to content

[Text] Implement CSS-Text-3 whitespace and SVG 2 wrap-in-shape.

This MR adds the final big pieces to our text layout: Using CSS-Text white-space handling of the white spaces and SVG 2 style text-in-shape.

The first one is a huge change, as it involves Krita not blindly stripping white-spaces it is unsure about. This will allow us to let artists create hard-breaks with the white-space:pre value (and also pre-wrap and pre-line). Notable here is that SVG 1.1 positioning only applies after white-space has been collapsed:

load_text-test-white-space

The second one is also a huge change: This required a first-position algorithm and a linebox algorithm. Furthermore, to ensure we can reuse as much code as possible between inline-size and flow-in-shape, I refactored the code thoroughly to introduce the concept of line boxes. This also required loading and saving the shapes to flow into, and to do this, I added some functions that only write paragraph-level properties.

load_textShape-test-complex-shapesload_textShape-test-text-align-justify

In my refactoring, I also fixed a number of issues:

  • Because we now follow the SVG spec on resolving the transforms instead of the older 'resolve-at-load', our SVG 1.1 texts are now exactly the same as how Chromium and Firefox render them (with exception of textPath-test-transforms.svg, which I adjusted so Arabic wasn't broken).
  • Baseline Alignment can now use the line boxes to align to text-top and text-bottom.
  • Line-height is now per-span and is calculated by using Harfbuzz functions where possible. This was necessary as line-height:normal may include a font-specific line-gap and does so in most implementations.
  • Text-indent each-line now works.
  • Overflow wrap got fixed, as well, the wrapping function doesn't try to count characters that would be collapsed if at the end of line, this leads to a much tighter line-wrap result.
  • I updated the selection of fonts a bit so that hardbreaks wouldn't cause trouble. This also adds 'default substitution', which should make the selection of fonts a little more in line with Qt's solution, though we're still not using the language tag.

Problems:

I haven't updated the render tests yet because I am not yet confident in the state of the code.

In particular, I'm worried about the content-area calculation code (QList<QPainterPath> KoSvgTextShape::Private::getShapes). I am currently using a very hacky way of using the QPainterPath code, based on how QPolygonF also uses QPainterPath (And QPathClipper) for boolean operations. This hacky way works by adding the fillPolygons of each shape as a QPolygonF to a QPainterPath. This seems clunky, but apparently the boolean operation code becomes a ton faster if the QPainterPath only contains straight lines.

load_textShape-test-shape-padding-margin

load_textShape-test-shape-inside-subtract

This is still slower than I'd like (pretty sure it's too slow for interactive use), but at the least the tests produce complete results now, and it is not as slow as the previous attempt. It'd be great to see if there's a faster (algebraic?) solution.

Finally, there's also that I need to update the line-height test [DONE], as well as add a test that covers mixed markup and the other wrapping features that are currently not tested for flow-in-shape [Also DONE].

Here's my current test-output: text-tests-output-2023-03-05.zip

Test Plan

  • Check old files if they still look correct.
  • Check out the new textShape svgs in the textTestSvgs folder.

Formalities Checklist

  • I confirmed this builds.
  • I confirmed Krita ran and the relevant functions work.
  • I tested the relevant unit tests and can confirm they are not broken. (If not possible, don't hesitate to ask for help!)
    • SEE ABOVE
  • I made sure my commits build individually and have good descriptions as per KDE guidelines.
  • I made sure my code conforms to the standards set in the HACKING file.
  • I can confirm the code is licensed and attributed appropriately, and that unattributed code is mine, as per KDE Licensing Policy.

Reminder: the reviewer is responsible for merging the patch, this is to ensure at the least two people can build the patch. In case a patch breaks the build, both the author and the reviewer should be contacted to fix the build. If this is not possible, the commits shall be reverted, and a notification with the reasoning and any relevant logs shall be sent to the mailing list, kimageshop@kde.org.

Edited by Wolthera van Hövell

Merge request reports