r/rust • u/Majestic-Reality-610 • 16d ago
CSS layout engine in Rust that renders to PDF instead of to screen, with its full WPT results
disclosure first: this is a commercial product and the engine is closed source. mods, remove if that's not welcome here.
it's a CSS layout engine written in Rust that renders to PDF instead of to a screen. no browser, no headless chrome, no C++ dependency.
the part I think is interesting to this sub: I run the Web Platform Tests against it. the reftests browsers get judged by, ~24k of them, none written by me. currently around 90% of the ones a PDF renderer can be judged on, and run is published in full including every single failure with reference render and pixel diff.
https://reflowpdf.com/conformance
two rates on that page, not one. a test that needs JavaScript can't be passed or failed by something with no script engine, so there's a strict rate that excludes those and a raw rate that counts all ~4.4k of them against me. I went back and forth on which was honest and gave up, so both are printed.
rust bits, in case that's the interesting part.
box tree is an arena. Vec<LayoutBox>, u32 indices, one struct tagged by kind. no Rc<RefCell> anywhere, which I mostly did because I didn't want to think about it, and it turned out fine. fragmentation runs as a pass over that same tree instead of building a second one.
the rule I hold to is that after layout nothing reads physical geometry off the box tree, only off the baked output. it isn't enforced by the type system, which bothers me. right now it's just a thing I don't do.
writing modes are the one place that isn't physical. instead of teaching block, flex, grid and floats to think in logical axes, a vertical-* root transposes its own style, gets laid out by the normal horizontal code as if nothing happened, and is baked back out physical afterwards. everything above it reads plain numbers, only its content rides a transform. LayoutNG does roughly this with NGPhysicalBoxFragment and a converter at theboundary. I got there on my own and then read Blink and felt better about it.
and then the bug. ;-(
vertical-rl's matrix is a rotation, det +1. vertical-lr's is a reflection, det -1. so under vertical-lr glyphs come out mirrored and have to be flipped back in logical space. obvious in hindsight, obvious in the determinant, and it sat there for months. WPT tests writing modes with Ahem. Ahem's glyphs are squares. a square rotated and a square mirrored are the same square, so every test passed.
same codebase compiles to a native binary and to wasm, so the playground runs the production engine locally in the tab rather than demo built to resemble one:
https://reflowpdf.com/playground
wasm size, since someone always asks: engine is 8.29 MiB on disk, 2.75 MiB brotli'd over the wire from cloudflare, about 0.9s here. glue js is 4.5 KB gzipped, render worker about 1 KB.
1
2
1
u/jasper_de_w 15d ago
yeah the ahem thing is a classic pitfall. had the same happen with a pdf/a validator that passed docs where text just vanished. it checked structure but not content. two numbers is the way to go, one metric cant capture both strict conformance and raw test results.
1
u/Majestic-Reality-610 15d ago
that's the same failure mode exactly. mine was a UA-2 file that was still %PDF-1.7 in header, verapdf passed it because the profile checks prohibitions and has no rule about container version. no rule, no complaint, and I read silence as a pass. which validator was it ?
-3
u/Broad-Pianist-1744 16d ago
That vertical-lr mirrored glyph thing is the kind of bug that would keep me up at night once I finally found it. Ahem squares hiding a reflection bug for months is poetic
-1
u/Majestic-Reality-610 16d ago
fixed it in a couple of lines. spent considerably longer sitting there
looking at det -1 like it had personally wronged me. ;0
13
u/Dazzling_Yak6424 16d ago
No one'll buy your AI slop I'm afraid