LeftAlignmentHandler spread each line's residual space across its word gaps,
clamped to max_spacing. A line whose residual divided to under max_spacing was
stretched flush, one that exceeded it was not, so left-aligned text was
justified sometimes, by a different amount per line - which reads as a wobbling
right edge rather than as ragged-right. Centre/right did the same, and computed
their start position from a different spacing than the one they returned, so
centred lines were not centred.
Ragged alignments now use a constant word space - the font's own space advance,
clamped to the style's bounds - and report overflow instead of tightening, so
line breaking decides what fits rather than rendering squeezing it.
Justification kept two further defects:
- the final line of a paragraph was stretched across the measure, so a
three-word tail was spread edge to edge. Line now carries is_paragraph_end,
set on the line holding the last word, and renders flush left. A paragraph
continued on the next page is not marked, so it stays justified.
- gaps were floored per gap with a truncated remainder, discarding the
fractional part of both. Lines stopped one or two pixels short, differently
each time. Distributing by cumulative rounding makes the gaps sum to the
residual exactly; advance ends now land identically on every line.
Alignment is configurable rather than hardcoded: PageStyle.default_alignment,
defaulting to JUSTIFY for body text. text_align on abstract and concrete styles
defaults to None meaning "unspecified", so HTML without text-align inherits the
page default while explicit CSS still wins. Headings are never justified.