Pre-flight checks
App version / commit
0.1.23-alpha (installer reports app-version=1.1.23)
Install method
GitHub Release desktop installer/package
Platform
Windows (desktop)
Desktop hardware
Custom PC, Ryzen 7 7800X3D, RTX 3080
OS version
Windows 11 Pro 26200
Area (tag)
Subtitles (download, styling, sync)
Bug description
Hebrew subtitles render with a left-to-right base paragraph direction. The Hebrew words themselves shape correctly right-to-left, but punctuation that ends a sentence — period, question mark, exclamation mark — is placed at the right edge of the line instead of the left.
A line whose logical text is יופי. נראה לי שזה טוב. displays with the closing period at the start of the visible line rather than at its end. Every line ending in punctuation is affected, which makes subtitles noticeably harder to read across a whole film.
This is not a font or encoding problem. The subtitle files are valid UTF-8 and carry no bidi control characters — one 85 KB Ktuvit .srt contains a single U+200F in the entire file — so the rendering depends entirely on the base direction the player resolves.
Android is not affected. PlayerSubtitleRtlFix.kt in composeApp/src/androidMain/ is applied to sidecar subtitles from PlayerSidecarSubtitles.kt. Desktop has no equivalent, because it renders through the native mpv/libass bridge rather than media3.
Root cause
libass resolves the base paragraph direction from the ASS style's Encoding field:
ass_resolve_base_direction(int enc):
enc == -1 -> FRIBIDI_PAR_ON // auto-detect per paragraph
otherwise -> FRIBIDI_PAR_LTR // "for compatibility with VSFilter"
That value reaches the shaper through ass_render.c:
state->font_encoding = style->Encoding; // ass_reset_render_context()
info->font_encoding = state->font_encoding; // parse_events()
When mpv converts an SRT to ASS it writes an ordinary charset number, never -1, so the base direction is always forced to LTR. Strong RTL runs still shape correctly, but trailing neutral characters resolve against the LTR paragraph base and land on the wrong side — exactly the observed artifact.
FriBidi and HarfBuzz are compiled in: MPVKit runs BuildFribidi() and BuildHarfbuzz() before BuildASS(). Nothing is missing from the build, the value is simply never set.
Steps to reproduce
- Play any movie on desktop.
- Load an external Hebrew
.srt subtitle (any addon, or a local file — I used Ktuvit and Wizdom subtitles).
- Wait for a subtitle line that ends in a period or question mark.
- Look at which side of the line the punctuation sits on.
Expected behavior
Sentence-final punctuation appears at the left edge of the line, as it does in other players and on the Android build.
Actual behavior
Sentence-final punctuation appears at the right edge of the line.
Frequency
Always
Did this work before?
No, it never worked
Media details (optional)
- Content type: movie
- Title: Michael (2026), tt11378946
- Subtitles: external Hebrew .srt (Ktuvit and Wizdom addons; also reproducible with a local file)
Logs (required for crash reports)
Anything else? (optional)
Suggested fix
Set Encoding=-1 on the ASS style via sub-ass-style-overrides, which restores per-paragraph auto-detection. mpv gates that list on the override level being non-zero:
if (shared_opts->ass_style_override[sd->order])
ass_set_style_overrides(ctx->ass_library, opts->ass_style_override_list);
applySubtitleStyle already sets sub-ass-override to either scale or force, both non-zero, so the override would apply in both subtitle modes.
The three desktop bridges need slightly different handling:
native/windows/player_bridge.cpp and native/macos/player_bridge.mm already build a change-list sub-ass-style-overrides command, but the libass branch passes clr and clears the list. The encoding override has to be set there too, not only alongside Bold in the custom-style branch.
native/linux/player_bridge.cpp never touches sub-ass-style-overrides at all — it only sets sub-ass-override and the individual sub-* properties — so it needs the override added rather than edited.
Auto-detection is per paragraph, so Latin subtitles are unaffected, and the same change fixes Arabic and Persian.
I can open a PR for this if you'd like.
Screenshot

Pre-flight checks
App version / commit
0.1.23-alpha (installer reports app-version=1.1.23)
Install method
GitHub Release desktop installer/package
Platform
Windows (desktop)
Desktop hardware
Custom PC, Ryzen 7 7800X3D, RTX 3080
OS version
Windows 11 Pro 26200
Area (tag)
Subtitles (download, styling, sync)
Bug description
Hebrew subtitles render with a left-to-right base paragraph direction. The Hebrew words themselves shape correctly right-to-left, but punctuation that ends a sentence — period, question mark, exclamation mark — is placed at the right edge of the line instead of the left.
A line whose logical text is
יופי. נראה לי שזה טוב.displays with the closing period at the start of the visible line rather than at its end. Every line ending in punctuation is affected, which makes subtitles noticeably harder to read across a whole film.This is not a font or encoding problem. The subtitle files are valid UTF-8 and carry no bidi control characters — one 85 KB Ktuvit
.srtcontains a single U+200F in the entire file — so the rendering depends entirely on the base direction the player resolves.Android is not affected.
PlayerSubtitleRtlFix.ktincomposeApp/src/androidMain/is applied to sidecar subtitles fromPlayerSidecarSubtitles.kt. Desktop has no equivalent, because it renders through the native mpv/libass bridge rather than media3.Root cause
libass resolves the base paragraph direction from the ASS style's
Encodingfield:That value reaches the shaper through
ass_render.c:When mpv converts an SRT to ASS it writes an ordinary charset number, never
-1, so the base direction is always forced to LTR. Strong RTL runs still shape correctly, but trailing neutral characters resolve against the LTR paragraph base and land on the wrong side — exactly the observed artifact.FriBidi and HarfBuzz are compiled in: MPVKit runs
BuildFribidi()andBuildHarfbuzz()beforeBuildASS(). Nothing is missing from the build, the value is simply never set.Steps to reproduce
.srtsubtitle (any addon, or a local file — I used Ktuvit and Wizdom subtitles).Expected behavior
Sentence-final punctuation appears at the left edge of the line, as it does in other players and on the Android build.
Actual behavior
Sentence-final punctuation appears at the right edge of the line.
Frequency
Always
Did this work before?
No, it never worked
Media details (optional)
Logs (required for crash reports)
Anything else? (optional)
Suggested fix
Set
Encoding=-1on the ASS style viasub-ass-style-overrides, which restores per-paragraph auto-detection. mpv gates that list on the override level being non-zero:applySubtitleStylealready setssub-ass-overrideto eitherscaleorforce, both non-zero, so the override would apply in both subtitle modes.The three desktop bridges need slightly different handling:
native/windows/player_bridge.cppandnative/macos/player_bridge.mmalready build achange-list sub-ass-style-overridescommand, but the libass branch passesclrand clears the list. The encoding override has to be set there too, not only alongsideBoldin the custom-style branch.native/linux/player_bridge.cppnever touchessub-ass-style-overridesat all — it only setssub-ass-overrideand the individualsub-*properties — so it needs the override added rather than edited.Auto-detection is per paragraph, so Latin subtitles are unaffected, and the same change fixes Arabic and Persian.
I can open a PR for this if you'd like.
Screenshot