The TEI Episodic Profile is formally documented in an ODD (One Document Does it
all) specification at schemas/tei-episodic-profile.odd. This specification:
- Defines the exact elements and attributes permitted in the profile
- Includes Schematron rules for validation constraints such as unique
xml:idvalues and speaker cross-referencing - Can be processed by TEI tools (Roma, TEI Stylesheets) to generate Relax NG and Schematron schemas for external validation
- Ships with a pre-generated Relax NG schema at
schemas/tei-episodic-profile.rng. Rust callers can retrieve it viatei_xml::relax_ng_schema()or write it to disk usingtei_xml::write_relax_ng_schema(path)before invoking external validators such asjing.
The profile supports:
- Header metadata: title, speaker declarations, annotation systems,
canonical citation declarations (
refsDecl/citeStructure/citeData), revision history - Body structure: paragraphs (
<p>), utterances (<u>) with optional speaker attribution via@whoplus local provenance attributes (@n,@source,@resp,@cert,@corresp,@ana), and thematic divisions (<div>) with@type(required and validated viaDivType), optional@subtype, optional@xml:id, and an optionalHeadwrapper for a single leading<head>element. Divisions can contain paragraphs, utterances, lists (<list>), and nested divisions. Lists hold ordered items (<item>) that carry optional@n(numbering or timestamp metadata),@corresp(pointer list for cross-references), and@xml:id. Each item may include an optional label prefix (<label>) followed by inline content. Paragraphs, utterances, items, labels, and heads all store orderedInlinenodes; plain strings can be constructed viaP::from_text_segments,Utterance::from_text_segments,Item::from_text_segments,Label::from_text, andHead::from_text. Lists are permitted within<div>elements only;<list>cannot appear directly as a child of<body>and must instead be wrapped in a<div>. Document validation also rejects duplicatexml:idvalues across nested division content and enforces declared-speaker checks when a profile cast is present.
Building divisions
Use the root tei_core re-exports to assemble a division tree before wrapping
it in a document body block:
use tei_core::{Div, Head, TeiDocument, BodyBlock};
fn build_episode_doc() -> Result<(), tei_core::TeiError> {
// Parent division.
let mut parent_div = Div::new("segment")?;
parent_div.set_subtype("chapter-markers")?;
parent_div.set_id("ch-01".to_string())?;
parent_div.set_head(Head::from_text("Chapter markers")?);
// Nested child division.
let mut child_div = Div::new("segment")?;
child_div.set_subtype("cold-open")?;
child_div.set_head(Head::from_text("Cold open")?);
// Attach the child and wrap the parent as a body block.
parent_div.push_div(child_div);
let header = tei_core::TeiHeader::new(tei_core::FileDesc::from_title_str(
"Episode outline",
)?);
let mut text = tei_core::TeiText::empty();
text.extend([BodyBlock::Div(parent_div)]);
let _document = TeiDocument::new(header, text);
Ok(())
}
- Stand-off overlays: root-level
<standOff>containers with<spanGrp>/<span>layers for many-to-many citation and analytical markup - Inline elements: emphasis (
<hi>with optional@rendattribute), pause markers (<pause>with optional@durand@type)
See schemas/README.md for instructions on generating schemas and validating
documents. In this profile, canonical citation declarations belong in
<encodingDesc><refsDecl>...</refsDecl></encodingDesc>, while citation and
provenance overlays that target multiple body nodes belong in the root
<standOff> section.