Files
craft-libs/crates/captions/tests/it/ttml_language.rs
Dylan M. Taylor 58b02d4e6a Add craft-captions: SRT, WebVTT, SCC, MCC, EBU STL and TTML behind one cue model (#876)
Carved from FilmCraft's filmcraft-captions (crates/captions at 4bec22bd): the
file readers and writers, CEA-608/708 coding, format detection and the cue
document. Rendering and the mapping onto FilmCraft's caption tracks stay there.

- time: the crate has its own exact Tick (1/254016000000 s) and rational
  FrameRate, the subset of filmcraft-time the formats use.
- TTML is read through craft-xml instead of roxmltree: no entity expansion,
  nesting bounded by the reader (128 levels, as before).
- parse refuses input over 64 MiB and keeps at most 1,000,000 cues.
- MCC and EBU STL name the caller's program (WriteOptions::program).
- plain_text (inline tag stripping) moves with the SCC/MCC writers that need it.
2026-11-11 16:23:34 -05:00

40 lines
2.0 KiB
Rust

use craft_captions::{Cue, Document, Format, TICKS_PER_SECOND, Tick, WriteOptions, parse, write};
fn cue() -> Cue {
Cue { start: Tick(TICKS_PER_SECOND), end: Tick(2 * TICKS_PER_SECOND), text: "Olá".into(), ..Default::default() }
}
/// TTML and DFXP exports write the document's language as `xml:lang`.
#[test]
fn ttml_and_dfxp_write_the_document_language() {
let doc = Document { cues: vec![cue()], language: Some("pt-BR".into()), ..Default::default() };
for format in [Format::Ttml, Format::Dfxp] {
let xml = String::from_utf8(write(&doc, format, WriteOptions::default())).unwrap();
assert!(xml.contains(r#"xml:lang="pt-BR""#), "{format:?}: {xml}");
// and the language comes back on import
let back = parse(xml.as_bytes(), format).unwrap();
assert_eq!(back.language.as_deref(), Some("pt-BR"), "{format:?}");
}
// a document with no language still writes a valid tag
let doc = Document { cues: vec![cue()], ..Default::default() };
let xml = String::from_utf8(write(&doc, Format::Ttml, WriteOptions::default())).unwrap();
assert!(xml.contains(r#"xml:lang="en""#), "{xml}");
}
/// An `xml:lang` that is empty or not a language tag leaves the language unknown.
#[test]
fn ttml_import_ignores_a_bad_xml_lang() {
let tt =
|lang: &str| format!(r#"<tt xmlns="http://www.w3.org/ns/ttml" xml:lang="{lang}"><body><div><p begin="1s" end="2s">Hi</p></div></body></tt>"#);
let doc = parse(tt(" fr-CA ").as_bytes(), Format::Ttml).unwrap();
assert_eq!((doc.language.as_deref(), doc.warnings.len()), (Some("fr-CA"), 0));
let doc = parse(tt("").as_bytes(), Format::Ttml).unwrap();
assert_eq!((doc.language, doc.warnings.len()), (None, 0));
for bad in ["pt BR", "&lt;x&gt;", &"a".repeat(36)] {
let doc = parse(tt(bad).as_bytes(), Format::Ttml).unwrap();
assert_eq!(doc.language, None, "{bad}");
assert_eq!(doc.warnings, ["ignored xml:lang: not a language tag"], "{bad}");
assert_eq!(doc.cues.len(), 1);
}
}