Skip to content

feat(font): add memory font registry and fallback support - #67

Merged
Shinokawa merged 7 commits into
AimesSoft:mainfrom
mom0ka27:feat/font-registry
Aug 3, 2026
Merged

feat(font): add memory font registry and fallback support#67
Shinokawa merged 7 commits into
AimesSoft:mainfrom
mom0ka27:feat/font-registry

Conversation

@mom0ka27

@mom0ka27 mom0ka27 commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Important

This PR is stacked on top of the subtitle style PR and is not ready to merge independently.

Please review and merge the feat/subtitle-style PR first. Once that lands, this branch will be rebased onto the target repository's latest default branch so this PR contains only the font registry and danmaku changes.

Summary

This PR splits the font registry and danmaku font work out of the subtitle style PR.

It adds validated custom font loading, preserves TTC/OTC face indices, and introduces a per-presenter in-memory font registry shared by subtitle and danmaku rendering.

The base branch is feat/subtitle-style, so this PR only contains the two font-specific commits:

  • d880c71 fix(font): validate custom fonts and preserve face index
  • f8b65bf feat(font): add memory font registry and fallbacks

Custom font validation

  • Limit path-based custom subtitle and danmaku fonts to 64 MiB.
  • Check file metadata before reading.
  • Reject directories, empty files, oversized files, and invalid font data.
  • Use bounded reads to avoid unbounded allocation if a file changes after the metadata check.
  • Preserve the existing fallback behavior when loading fails.
  • Emit structured diagnostics for rejected custom fonts.
  • Reload subtitle fonts when the contents at the same path change.

TTC/OTC support

  • Preserve the face index returned by fontdb.
  • Load the configured face instead of always selecting face 0.
  • Apply the same face-index handling to explicit paths, family lookups, and default font lookup.
  • Add regression coverage using a multi-face collection.

Memory font registry

Each presenter now owns an isolated memory font registry.

The registry supports:

  • Registering font bytes and returning a font ID.
  • Validating and enumerating all faces in TTF/TTC/OTC data.
  • Selecting an ordered list of font IDs as the fallback chain.
  • Clearing registered fonts.
  • Querying registry status and selected IDs.
  • Querying face metadata, including:
    • face index
    • family names
    • PostScript name
    • weight
    • italic state
    • monospaced state

Resource limits:

  • 32 MiB per in-memory font.
  • 128 MiB total per presenter.

Subtitle integration

  • Register selected memory fonts with libass through ass_add_font.
  • Preserve selection order as fallback priority.
  • Apply the registry to both plain-text and native ASS subtitle paths.
  • Rebuild the ASS renderer when the font generation changes.
  • Replay cached ASS chunks into a candidate renderer before swapping it in, so a failed font update does not discard active subtitle events.
  • Include the font generation in the plain-text subtitle renderer cache key.

Danmaku integration

  • Share the selected memory-font snapshot with synchronous and asynchronous danmaku layout.
  • Resolve missing glyphs in the configured selection order.
  • Keep an explicitly configured danmaku font at the highest priority.
  • Use selected memory fonts before the existing bundled/system fallback.
  • Atomically invalidate prepared layouts, glyph caches, text-layout caches, and the glyph atlas when the font generation changes.
  • Reject stale asynchronous plans created with an older font generation.

C API

Adds APIs for:

  • Registering memory font bytes.
  • Selecting an ordered list of font IDs.
  • Clearing registered fonts.
  • Querying registry status.
  • Querying font and face information.

The status and info APIs include matching release functions for dynamically allocated result data.

Flutter

Adds:

Future<int> registerSubtitleMemoryFont(Uint8List data)
Future<void> selectSubtitleMemoryFonts(Iterable<int> fontIds)
Future<void> clearSubtitleMemoryFonts()
Future<ErikaSubtitleMemoryFontStatus> getSubtitleMemoryFontStatus()

The APIs are bridged on Android, iOS, macOS, and Windows. This allows fonts from Flutter assets or application-managed storage to be registered without exposing a platform-private font path to the Rust core.

Apple platforms

This PR does not change the Apple/libass font-provider strategy.

On recent Apple systems, applications cannot access system-private font directories directly. The memory registry supplements the existing behavior with fonts supplied by the application, such as Flutter assets, user-installed fonts, downloaded fonts, and fonts stored in the application bundle or sandbox.

Verification

  • cargo fmt --all -- --check
  • cargo test -p erika --lib
  • cargo test -p erika_capi --lib
  • cargo check -p erika --no-default-features --features libass,wgpu
  • flutter test
  • flutter analyze
  • iOS Swift frontend parse
  • macOS Swift frontend parse
  • git diff --check

Additional regression coverage includes:

  • Oversized, empty, directory, and invalid font rejection.
  • Same-path subtitle font replacement.
  • TTC/OTC face-index preservation.
  • Ordered memory-font fallback.
  • Font-generation cache invalidation.
  • Synchronous/asynchronous danmaku snapshot consistency.
  • C API registration, selection, status, and info round trips.
  • Flutter typed-data and font-ID forwarding.

@mom0ka27
mom0ka27 marked this pull request as draft July 29, 2026 10:32
@mom0ka27
mom0ka27 force-pushed the feat/font-registry branch from f8b65bf to d4e849e Compare July 29, 2026 12:57
@mom0ka27
mom0ka27 marked this pull request as ready for review July 29, 2026 14:13

@Shinokawa Shinokawa left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Found one correctness issue in ASS memory-font cache invalidation.

Comment thread crates/erika/src/presenter.rs

@Shinokawa Shinokawa left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The memory-font revision fix looks right, and the regression test covers it. Thanks.

Blocking on something else: the Merge branch 'main' commit (303ddb4) resolved conflicts in favour of the branch and dropped code that is on main. e2e2099 put the debug_hud field and its two accessors back so erika_capi compiles, but the features themselves are gone — git diff origin/main...HEAD -- crates/erika/src/presenter.rs is +423/-1047, and none of these exist anywhere in the branch any more:

  • debug_hud_snapshot and the HUD render path (d408842) — debug_hud is now a dead field, enabling the HUD draws nothing
  • refresh_video_decoder_status (d3e45aa)
  • trace_current_danmaku_motion, danmaku_motion_backstep, DanmakuMotionSample (dc64ae8)
  • retain_danmaku_state_for_config_change, danmaku_viewport_requires_relayout, bump_danmaku_generation_for_config_change, invalidate_current_danmaku_plan, set_paint_config (7e27c80)
  • roughly 11 tests that went with them, including screenshot_capture_context_omits_danmaku and CaptureCompositionProbe

CI is green because the call sites were removed along with the definitions, so nothing fails to compile.

Could you redo the merge from current main and reapply only the font changes on top? A quick check before pushing: git diff origin/main...HEAD -- crates/erika/src/presenter.rs should contain no deletions outside the font/subtitle paths.

mom0ka27 and others added 6 commits July 31, 2026 18:34
The subtitle style and memory font registry work reintroduced platform cfg
lists without `target_env = "ohos"`, which OpenHarmony needs (its `target_os`
is `linux`). That left the whole new API surface — `set_subtitle_font`,
`set_subtitle_style`, the `*_subtitle_memory_font*` entry points, their
`*_to_c` helpers and `set_output_headroom` — either undefined on OpenHarmony
or defined twice alongside its stub.

Restores the predicate so every impl/stub pair is an exact complement again.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…clear()

After memory fonts are installed, switching to another ASS track calls
clear() which left memory_font_revision unchanged. process_frame() then
created a new renderer without memory fonts, while render() saw the old
revision as current and skipped the rebuild. Reset memory_font_revision
to 0 in clear() so render() detects the mismatch and rebuilds with the
current memory-font snapshot.
… ass_set_fonts

Extended LibassFontSelection with memory_family so configure_style can
cache the full font selection (family, default_font, memory_family) and
skip ass_set_fonts when nothing changed. This prevents redundant font
selector rebuilds on every play_res_height / font_scale / style change.
@mom0ka27
mom0ka27 force-pushed the feat/font-registry branch from a52cf11 to 78bd1a5 Compare July 31, 2026 10:40
@Shinokawa
Shinokawa merged commit c6d089f into AimesSoft:main Aug 3, 2026
11 of 12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants