pub struct Stl {
pub gsi: GsiBlock,
pub ttis: Vec<TtiBlock>,
}Expand description
A subtitle list in the EBU STL format: a GSI block followed by TTI blocks.
Fields§
§gsi: GsiBlockThe General Subtitle Information (GSI) block
ttis: Vec<TtiBlock>The Text and Timing Information (TTI) blocks, in file order
Implementations§
Source§impl Stl
impl Stl
Sourcepub fn validate(&self) -> Vec<ValidationIssue>
pub fn validate(&self) -> Vec<ValidationIssue>
Checks the subtitle list against the rules in the spec, and returns the deviations found. An empty list means that no deviation was found.
The fields are checked one at a time as by the validating accessors, e.g.
GsiBlock::creation_date, and together:
- TND is 1-9 and DSN 1 up to TND.
- TNB, TNS and TNG match the TTI blocks, when the subtitle list is on a single disk.
- TCF is the TCI of the first subtitle.
- Subtitle Numbers are continuous and ascending, and TCI ascending within a subtitle group.
- Extension blocks have ascending EBN, the last block of a subtitle has EBN FFh and its Text Field ends with unused space (8Fh). EBN F0h-FDh are reserved.
- TCI, TCO, JC and CF of the first block of each subtitle are valid, and TCO is not before TCI.
- VP is 1-23 for teletext and at most MNR for open subtitles.
- The Text Field only has control codes for the Display Standard Code, and no reserved codes.
Time codes are expected to increase with time, as the spec requires ascending TCI (section 4.3.1). A subtitle list that passes midnight, where time codes start over from 00:00:00:00, or that has cuts in the time codes is therefore reported. Comment subtitles (CF 01h) are not included in the time code order and TCF.
§Example
use ebustl_parser::{Stl, Time, TtiFormat};
let time = |seconds| Time { hours: 10, minutes: 0, seconds, frames: 0 };
let format = |vp| TtiFormat { jc: 2, vp, dh: false };
let mut stl = Stl::new();
stl.add_sub(time(1), time(3), "Valid", format(20)).unwrap();
assert!(stl.validate().is_empty());
// Teletext has rows 1-23
stl.add_sub(time(4), time(6), "Below the screen", format(30)).unwrap();
let issues = stl.validate();
assert_eq!(1, issues.len());
assert_eq!("VP", issues[0].field());
assert_eq!(Some(1), issues[0].tti_index());Source§impl Stl
impl Stl
Sourcepub fn new() -> Stl
pub fn new() -> Stl
Creates an empty subtitle list with a default GSI block, see GsiBlock::new.
Sourcepub fn write_to_file<P: AsRef<Path>>(
&self,
filename: P,
) -> Result<(), ParseError>
pub fn write_to_file<P: AsRef<Path>>( &self, filename: P, ) -> Result<(), ParseError>
Writes the subtitle list to a file.
§Errors
Fails if the file cannot be written, or if a text field in the GSI block cannot be encoded with its code page.
§Example
use ebustl_parser::{Stl, Time, TtiFormat, parse_stl_from_file};
let time = |seconds| Time { hours: 10, minutes: 0, seconds, frames: 0 };
let mut stl = Stl::new();
stl.add_sub(time(1), time(3), "Hello", TtiFormat { jc: 2, vp: 20, dh: false })
.expect("Add subtitle");
let path = std::env::temp_dir().join("ebustl_parser_write_to_file_example.stl");
stl.write_to_file(&path).expect("Write file");
let written = parse_stl_from_file(&path).expect("Parse file");
assert_eq!("Hello", written.subtitles().next().unwrap().text());Sourcepub fn add_sub(
&mut self,
tci: Time,
tco: Time,
txt: &str,
opt: TtiFormat,
) -> Result<(), ParseError>
pub fn add_sub( &mut self, tci: Time, tco: Time, txt: &str, opt: TtiFormat, ) -> Result<(), ParseError>
Adds a subtitle after the existing TTI blocks and updates TNB and TNS.
Rows are separated by \n or \r\n. Text that does not fit in one TTI block continues
in extension blocks. Teletext control codes boxing each row, and double height if
opt.dh is set, are added unless the Display Standard Code is open subtitling.
The Subtitle Number continues from the last block, starting at 1. The first subtitle sets the Time Code: First In-Cue (TCF) of the GSI block.
§Errors
Returns an error, leaving the Stl unchanged, if the text needs more than 241 TTI
blocks, if TNB would exceed 99 999 or if the Subtitle Number would exceed FFFFh.
§Example
use ebustl_parser::{Stl, Time, TtiFormat};
let time = |seconds| Time { hours: 10, minutes: 0, seconds, frames: 0 };
let format = TtiFormat { jc: 2, vp: 20, dh: false };
let mut stl = Stl::new();
stl.add_sub(time(1), time(3), "First row\nSecond row", format)
.expect("Add subtitle");
let subtitle = stl.subtitles().next().unwrap();
assert_eq!("First row\r\nSecond row", subtitle.text());
assert_eq!(1, stl.gsi.total_number_of_subtitles());Sourcepub fn subtitles(&self) -> Subtitles<'_> ⓘ
pub fn subtitles(&self) -> Subtitles<'_> ⓘ
Iterates over the subtitles, each made up of one or more TTI blocks.
Consecutive blocks with the same Subtitle Number form one subtitle. A subtitle ends at its last block (EBN FFh), although a following user data block (EBN FEh) with the same Subtitle Number still belongs to it.
§Example
use ebustl_parser::parse_stl_from_file;
let stl = parse_stl_from_file("stls/test.stl").expect("Parse stl from file");
let texts: Vec<String> = stl
.subtitles()
.filter(|subtitle| !subtitle.is_comment())
.map(|subtitle| subtitle.text())
.collect();
assert_eq!(" dans la baie de New York.\r\n", texts[11]);