5.26.0 - Text subtitle styling
Minor release. Text subtitle styling stops being thrown away, and the PiP compositor renders it.
Added
Text subtitle styling and placement reach the host. (#233)
SubtitleTextRun carried a colour and nothing else, so bold, italic, font face and everything past colour was flattened out of every text subtitle before a host could see it. It now carries isBold, isItalic, isUnderlined, isStruckThrough, fontName and fontSize, and SubtitleCue carries placement: a SubtitleTextPlacement holding an ASS numpad alignment and an optional anchor normalized to [0, 1], the same convention SubtitleImage.position already uses. The new initializer parameters are defaulted, so existing call sites compile unchanged.
The interesting part is that none of it needed per-format parsing, because the markup was already arriving. libavcodec converts every text subtitle format into an ASS event line before the engine sees it, and AVSubtitleRect.ass was carrying the tags the whole time:
- SRT goes through
ff_htmlmarkup_to_ass(libavcodec/htmlsubtitles.c), which maps<b>/<i>/<u>/<s>to{\b1}/{\i1}/{\u1}/{\s1},<font color=...>to{\c&HBBGGRR&},<font size=...>to{\fs<n>},<font face=...>to{\fn<name>}and<br>to\N. - WebVTT maps its inline tags to the same overrides.
- dvb_teletext already decoded with
txt_format=ass, which is why its colours alone had survived since #107.
What discarded the rest was on this side. cleanASSBody stripped every {...} block with a regex, and the colour parser handled \c / \1c and documented, accurately, that "All other override tags are ignored". That parser is now a full override parser, so SRT, WebVTT, teletext and ASS all light up through one code path, and the teletext positioning that was arriving and being dropped comes along with it.
Lookalike tags are left alone rather than half-parsed: \be and \bord are not \b, \iclip is not \i, \shad is not \s, \fscx and \fsp are not \fs. \r resets the accumulated state. \an and \pos are cue-level and are lifted out rather than splitting a run, and \pos is normalized against the play resolution the line actually uses: a real ASS script's declared PlayResX/Y when the header provides one, otherwise the 384x288 default (ASS_DEFAULT_PLAYRESX/Y) that libavcodec's synthesised lines use.
No option gates this. An unstyled cue still arrives as .text carrying the same string it always did, so a host handling only that case sees no change, and .richText was already reachable through teletext for anything rendering subtitles. SubtitleCue.text still flattens either body to plain text.
The software-path PiP compositor renders it. (#233)
It previously reduced a rich-text cue to runs.map(\.text).joined() and drew every cue in one white font stacked from the bottom, so the styling would have stopped at the PiP window. It now builds a per-run attributed line, with per-run colour, font face, ASS-relative size, real CoreText bold and italic traits, underline, and a manually drawn rule for strikeout, which CoreText has no attribute for. Placement is honoured: an ASS numpad alignment maps to a corner inset by the layout margin, an explicit \pos anchors the block on the point according to that alignment. Cues without placement keep the existing bottom-up stack.
Notes
WebVTT cue settings (line, position, align, size, vertical) still do not arrive. libavcodec does not convert them, as webvttdec.c states in its own file header:
* @todo need to support extended markups and cue settingsWebVTT bold, italic and underline work through the above; WebVTT positioning needs an upstream change. SRT positioning arrives only when the demuxer supplies AV_PKT_DATA_SUBTITLE_POSITION side data, which most .srt files do not carry, though a leading {\anN} written into the text survives libavcodec's conversion.
The preserveASSMarkup path is untouched: it still hands over raw event lines and the script header for hosts driving their own libass renderer.
Requested by tresby (#233).