More Layout

Over time I’ve been working to extend CatTrap more of the modern CSS3 layout formulas, and have now reached a point where I’ve implemented all the main ones I care about! So now’s as good as any to discuss these CatTrap enhancements!

Table Layout

HTML tables provides an invaluable way to take advantage of 2D screens to denote how data relates to each other, & I’ve already implemented CSS3 Grid in part due to how similar it is to table layout. Now I’ve updated my CSS style tree to parse <table>s to lower them into the same layout nodes. Wrapped in a block node for the CSS Box Model & captions. Not that CatTrap examines the HTML tree directly, instead the useragent stylesheet ensures all necessary info is transferred over via the style tree. This is basically the standardized approach here, which I’ve been embracing elsewhere too!

To be clear: CSS3 Grid does take significant logic to make sense of its CSS properties, perhaps more than <table> does. That’s not the code I’m reusing!

Individual cells are positioned using a counter, though the rowspan attribute complicates this a little. A list of countdowns is used to dodge those nodes when placing the start of table cells. Afterwards table rows/columns and groups thereof are laid out as background nodes accross the full width or height of the table, as we instruct the grid layout formulas how many cells wide & tall the table is. Once those dimensions are known. Recovering from poorly-structured pages (which, say, include <td>s not contained directly within a <table> & <tr>) was necessary to avoid crashes, which was implemented by inferring the missing layout nodes.

Flex Layout

The tricky bit of integrating Flex Layout into CatTrap is that it can position its children primarily-along either axis. With different pagination routines required for both. And with different integrations into the general width-then-height tree-traversal layout routines.

Most of the logic is triggered upon knowing the “base axis”, at which we point the children need to be optionally-wrapped into “lines” & aligned within those lines. From there positioning is a running sum. An optional preprocessing loop computes a running sum on the row to split it up into multiple, whilst postprocessing may reverse those rows and/or reallocate underflowing or overflowing spacing.

Paginating horizontal flexboxes is easy as overflowing lines can be placed on the next page, this may even be a fallback for vertically flexboxes! However for vertical flexboxes, we might hold off on laying them out until then, since the page height is the tightest constraint on their height.

Handling Downstream Lengths

As I developed Mondrian I found that far more CSS properties are concerned with lengths than those properties CatTrap implements. To simplify supporting all the length units in downstream components like Mondrian I added some infrastructure to resolve length units in arbitrary properties. This also allows for tighter cohesian when parsing CSS shorthands.

If a downstream parser for a given CSS property indicates that it supports px units, then CatTrap’s property parser will convert any length unit with screen pixels. Buffering the tokenized CSS until we’ve selected a font, since some units are based on metrics from that font.

Improved Text Layout

Balkón has been updated to support more formatting options. As Jaro has previously described this required a change in Balkón’s API. So CatTrap’s integration of it got rewritten to match. This includes:

Also all the routines for sizing text needed to be altered, and we needed to ensure that the appropriate measurements were used for this. Ensuring text can overflow its allocated bounding box.

Inline Blocks & Replaced Elements

We may want to render images inline with text, or we may want to do so for other block elements. Adding support for this required:

  1. Adjusting the layout tree datamodel to be able to store the inline elements.
  2. Adding a new display mode wrapping any other, & adjusting the lowering of inline-runs to handle it.
  3. Ensure the inline-layout code consults its inner layout node(s) to tell Balkón to reserve space for it.

I didn’t directly implement support for embedded images, etc. But it should be trivial to implement as a PropertyParser wrapping CatTrap’s & Mondrian’s ones, lowering the image to the background-image CSS property.


I think CatTrap now supports all the layout formulas I care the most about. Though I think I’m not particularly pleased with the software architecture I implemented them in terms of.

Richtext layout is an inherently complex system with non-trivial interactions between the different formulas. I’ve learnt a lot about how this works, but the infrastructure I built to express it in terms of doesn’t appear to be doing a good enough job. Leaving me to make the difficult call between pushing forwards to get a running program or to get back & tidy up my mess. I would appreciate some help here!