CEA-608 has no code for some characters (the ellipsis, CJK) and the SCC
and MCC writers dropped them, or sent a close basic-set one (’ as '),
without saying so. The CEA-708 service in MCC sent ? the same way, for
instance for an em dash.
charset_warnings lists each such character once, with how often and
the first caption that has it, using the writers' own tables, and
captions.export returns the list as warnings. The list is capped at
20 characters per stream plus one summary line.
Co-authored-by: Brandon Thomas <echelon@gmail.com>
TTML and DFXP exports always wrote xml:lang="en", whatever the caption
track's language. The document now carries the language: export takes
the track's, TTML import reads xml:lang (a language tag of at most 35
characters, else a warning), and captions.import takes an optional
language that wins over both.
The menu item, and the Text panel's Export captions…, opens Caption Export
Settings with a File Format menu of every format captions.export writes. OK
opens the save panel with only that format's filter and a matching name. A
name typed with another caption format's extension is written in that format
(it is the file the save panel asked about); any other name gets the chosen
format's extension. If a file with that new name already exists, nothing is
written: with WebVTT chosen, typing "keep" when keep.vtt exists leaves
keep.vtt alone. With a path the command still exports directly.
Also fixes a panic: captions.export with params that are not an object
indexed into them.
Fixes#3365
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Caption text was always the family's SemiBold: caption_style hard-coded
the style. CaptionStyle gains font_style (captions.setStyle `fontStyle`,
default "SemiBold", serde default so old projects load unchanged),
resolved like graphic text: the nearest weight when the family has no
face of that name, SemiBold when empty.
- render_caption uses it (Program monitor and burned-in export)
- captions.list reports it; the Text panel's Captions tab offers the
family's styles next to the font
- Upgrade Caption to Graphic carries it to the text layer's font style;
graphics.fonts.used lists it and file.replaceFonts matches and
replaces it
- docs/captions.md, docs/graphics.md
The default keeps default_style_renders_like_the_reference green
(pixel-identical); font_style_is_used renders Regular and Bold against
SemiBold.
Fixes#3357
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Text panel: the Captions list follows the playhead
As the playhead reaches a caption, the Captions list scrolls it to the middle, so the list stays
lined up with the Timeline during playback and scrubbing. Each caption is brought into view once
(scrolling the list by hand still works), and never while a field in the panel has focus.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Move the Captions list follow test into the shared ui-egui test binary
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* Edit: Copy, Cut and Paste work on captions selected in the Timeline
With only captions selected, Edit ▸ Copy / Cut (⌘C / ⌘X) copy them to a caption clipboard,
and Paste / Paste Insert (⌘V / ⇧⌘V) place them at the playhead on the caption tracks they came
from (the last track when that one is gone), overwriting what is there; Paste Insert first pushes
later captions on by the copied span. The pasted captions are selected, the playhead moves past
them, and it is one undo step. Copying clips empties the caption clipboard and the other way
round, so Paste places whichever was copied last. Before, Copy did nothing with only captions
selected.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Move the caption copy/paste UI test into the shared ui-egui test binary
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* Timeline: Option-drag duplicates captions
Option-dragging a caption block leaves the original in place and drops a copy where it is
released; the cursor turns into a copy cursor and the original stays drawn under the copy while
dragging. A drag now carries the whole caption selection, not only the grabbed block.
New command `captions.duplicate` {captions, delta|deltaFrames}: copies on their own tracks,
never before 0, selected afterwards, one undo step. Captions never overlap, so
`edit::captions::overwrite_captions` places copies over what is there: captions under a copy are
trimmed, split around it or removed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* captions.duplicate: refuse an empty caption list and copy each caption once; move its UI test into the shared ui-egui test binary
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* Timeline: the Razor tool cuts captions
With the Razor tool (C), a click on a caption block in the Timeline splits it there, snapped like
a clip cut, with the same white preview line; Shift cuts every caption on an unlocked caption
track at that time. Before, the caption blocks took every click for select/move whatever the
tool, so the Razor did nothing on them. Locked caption tracks ignore it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Move the caption Razor UI test into the shared ui-egui test binary
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
The interface had no face for Arabic or Hebrew, so caption text, track and media names showed
missing-glyph boxes in the Text panel and on the timeline while the Program monitor drew them.
crates/ui-egui/src/rtl.rs adds the craft-fonts Arab / Hebr faces, else one installed face per
script (Noto Sans Arabic, SF Arabic, Segoe UI...), as the last fallbacks of every UI font family,
as cjk.rs does for Chinese.
egui already shapes each font run with harfrust, so the letters join, but it lays runs out left to
right with no bidi pass and multi-word Arabic read backwards. rtl::Lines lays captions out in
reading order: lines are broken on the logical text as egui wraps it, each line is reordered with
unicode-bidi (UAX #9, as crates/text does for titles) and every right-to-left word is its own
layout section for egui's shaper. Caption blocks on the timeline and the Text panel caption list
use it; a caption being edited keeps egui's logical order, since TextEdit places the caret by
character index. Left-to-right text is laid out exactly as before.
`captions.list` resolved `track` with track_param and treated an
unresolved selector the same as an omitted one, so a missing numeric id
(or a label past the last track, such as `C3` with two tracks) returned
every caption track with success. It now fails with "no such caption
track", as captions.export, setTrack, setStyle, deleteTrack and add do.
Omitting `track` still lists every track, and valid ids and C-labels
still return just their track.
Test: two caption tracks; each id and each label returns only its own
track; a missing id, `C3`, `C0` and a non-label string are errors.
Fixes#545
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
docs/captions.md still said nested sequences do not burn in their own
captions. Since NEST10 the captions of a nested sequence are drawn wherever
the nest is shown, in the Program monitor and in exports.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
captions.setStyle {margin: 60} (pixels, as one would guess) was silently clamped to 0.45 and
put subtitles mid-frame. The parameter doc and docs/captions.md now give the unit and range.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- New L1 crate filmcraft-text: fonts (bundled Inter Regular/Medium/SemiBold/Bold/Italic,
JetBrains Mono, Noto Serif; DirectorySource scans system font folders reading only name/OS2
tables; family/style resolution with synthetic bold/italic; per-char fallback), layout
(harfrust kerning/ligatures, unicode-bidi, unicode-linebreak, alignment incl. justify,
leading, tracking, baseline shift, all/small caps, underline, carets/hit-test/selection,
cached), raster (moved from captions; active-edge list, shapes, emboldening), render
(outline + sub-pixel glyph-mask caches; transformed-outline path for rotation/skew),
mask (distance-transform strokes, blur).
- Captions burn-in now shapes and kerns via the engine (font.rs/raster.rs removed).
- Timecode / Clip Name effects draw real text (full clip names, not seven-segment digits);
text_burnin golden re-blessed.
- UI takes its font bytes from the text crate (no duplicate copies in the binary).
- New fonts: Inter Bold, Inter Italic (OFL-1.1, rsms/inter v4.1), Noto Serif Regular
(OFL-1.1, notofonts) with sidecars, ATTRIBUTION.md rows and licence text.
- Perf: 3-line 96 px title at 1080p ~1.6 ms cold / 0.15 ms warm (release).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>