Work in progress — this reference is being written in the open. Unfinished pages are excluded from search engines.
Paged · IDML Reference
Stories & text

Story structure

The element model of a story — the Story → ParagraphStyleRange → CharacterStyleRange → Content nesting, and the attributes the renderer reads.

Pro· reference

A story is a tree four levels deep: StoryParagraphStyleRangeCharacterStyleRangeContent.

In short: Every IDML story nests the same four levels. The outer Story element carries the id a frame points at; each paragraph is a ParagraphStyleRange; each run of uniform character formatting is a CharacterStyleRange; and the literal text lives in Content elements, interrupted by Br and Tab markers. This page is the reference for that model — the elements, the attributes our parser reads, and the constructs it reads but doesn't yet fully place.

A story is a tree four levels deep. The outer Story element carries the id a frame points at; inside it, each paragraph is a ParagraphStyleRange; inside each of those, one or more CharacterStyleRange elements hold runs of uniform character formatting; and inside those, the literal text lives in Content elements, interrupted by Br and Tab markers. The parser reads exactly this shape — every other story attribute it doesn't yet act on is read past.

Story
└── ParagraphStyleRange        one paragraph
    └── CharacterStyleRange    one run of uniform character formatting
        ├── Content            literal text
        ├── Br                 forced line break
        └── Tab                tab character

The example below is one story with two paragraphs. Each paragraph is its own ParagraphStyleRange; the second carries a local SpaceBefore override. The renderer reports paragraphs: 2, runs: 2 for it.

Two ParagraphStyleRange blocks in one story — two paragraphs, each with one run.

Stories/Story_ustory.xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<idPkg:Story xmlns:idPkg="http://ns.adobe.com/AdobeInDesign/idml/1.0/packaging" DOMVersion="20.0">
  <Story Self="ustory">
    <ParagraphStyleRange AppliedParagraphStyle="ParagraphStyle/$ID/[No paragraph style]">
      <CharacterStyleRange AppliedCharacterStyle="CharacterStyle/$ID/[No character style]">
        <Content>The first paragraph stands on its own.</Content>
      </CharacterStyleRange>
    </ParagraphStyleRange>
    <ParagraphStyleRange AppliedParagraphStyle="ParagraphStyle/$ID/[No paragraph style]" SpaceBefore="6">
      <CharacterStyleRange AppliedCharacterStyle="CharacterStyle/$ID/[No character style]">
        <Content>The second paragraph follows it.</Content>
      </CharacterStyleRange>
    </ParagraphStyleRange>
  </Story>
</idPkg:Story>

Story

The root element of a Stories/Story_*.xml part. Its Self id is the value a text frame names in ParentStory, and the value the design map lists in StoryList. The parser surfaces the writing direction today and reads the rest past — additional story-level attributes land in later parser work.

Attribute · StoryType / valuesSupportNotes
Selfstring idSupportedThe story id a frame points at via ParentStory.
StoryDirectionLeftToRightDirection | RightToLeftDirectionSupportedWriting direction for the whole story.
AppliedTOCStylestring refNot yet parsedRead past; not acted on.
TrackChangesbooleanNot yet parsedRead past; tracked changes are not surfaced.

A Story may also hold a StoryPreference child; the parser reads OpticalMarginAlignment and OpticalMarginSize from it.

ParagraphStyleRange

One paragraph. A new ParagraphStyleRange sibling starts a new paragraph — that is what makes paragraph count equal to the number of these blocks, not a count of line breaks. AppliedParagraphStyle names the style; any attribute set directly on the element is a local override that wins over the style for this paragraph.

Attribute · ParagraphStyleRangeType / valuesSupportNotes
AppliedParagraphStylestring ref (ParagraphStyle/…)SupportedThe paragraph style applied; resolved against Styles.xml.
JustificationLeftAlign | CenterAlign | RightAlign | LeftJustified | CenterJustified | RightJustified | FullyJustified | ToBindingSide | AwayFromBindingSideSupportedAlignment override. FullyJustified is composed as LeftJustified; the binding-aware values fall back to Left/Right until binding side is plumbed through.
SpaceBeforedouble (pt)SupportedSpace added above the paragraph.
SpaceAfterdouble (pt)SupportedSpace added below the paragraph.
FirstLineIndentdouble (pt)SupportedIndent of the paragraph’s first line.
DropCapCharactersshort (0–150)SupportedNumber of leading characters to set as a drop cap.
DropCapLinesshort (0–25)SupportedHow many lines tall the drop cap is.
DropCapDetailintSupportedDrop-cap sizing/positioning detail; read alongside the two counts.
BulletsAndNumberingListTypeNoList | BulletList | NumberedListSupportedWhether the paragraph carries a bullet or a number.
OverprintFillbooleanSupportedParagraph-level fill overprint; inherits from the cascade when absent.

A ParagraphStyleRange can also carry a TabList of TabStop children and (when the paragraph hosts one) a Table; tabs are covered alongside the run model below.

CharacterStyleRange

One run of uniform character formatting inside a paragraph. A new CharacterStyleRange starts wherever the character formatting changes — bold to regular, one size to another. Like paragraphs, the count of runs equals the count of these blocks. AppliedCharacterStyle names the style; direct attributes are local overrides. Some character properties (AppliedFont, Leading) are carried as typed children inside a Properties element rather than as attributes, and the parser reads both forms.

Attribute · CharacterStyleRangeType / valuesSupportNotes
AppliedCharacterStylestring ref (CharacterStyle/…)SupportedThe character style applied; resolved against Styles.xml.
PointSizedouble (pt)SupportedType size for the run.
AppliedFontstring (font family) — Properties childSupportedFont family. Carried as a typed child of Properties; FontStyle names the face.
FontStylestringSupportedThe named face within the family (e.g. "Bold").
FillColorstring ref (Color/… or Swatch/None)SupportedGlyph fill swatch; resolved against Graphic.xml.
FillTintdouble (0–100, or -1)SupportedTint percentage of the fill; -1 / absent means use the swatch as-is.
Trackingdouble (1/1000 em)SupportedUniform spacing added to every glyph’s advance.
Leadingdouble (pt) — Properties childSupportedLine leading. Absent means Auto (point size × 1.2).
UnderlinebooleanSupportedDraws an underline under the run.
StrikeThrubooleanSupportedDraws a strikethrough through the run.
BaselineShiftdouble (pt)SupportedRaises (+) or lowers (−) glyphs from the baseline.
AppliedConditionsspace-separated Condition/… refsSupportedWhen all referenced conditions resolve to Visible, the run is laid out; if any is hidden, the run is dropped.

Content, Br, Tab

The leaves of the tree. Content holds literal text and is the only place text lives. Br is a forced line break inside the paragraph; the parser turns it into a newline in the run. Tab is a tab character. A run can hold any number of these in sequence — several Content chunks split by Br/Tab collapse into one run string with \n / \t in place.

Attribute · Content / Br / TabType / valuesSupportNotes
Contentelement with text bodySupportedThe literal characters. U+2028 / U+2029 inside it are normalised to newlines.
Brempty elementSupportedForced line break — becomes a newline in the run text.
Tabempty elementSupportedTab character — becomes a tab in the run text. TabStop positions live on the paragraph’s TabList.

What the story tree carries but the renderer doesn't fully place

A CharacterStyleRange is also where IDML hangs several constructs the parser reads but the renderer does not yet fully lay out. They are detailed on the threading and overset and text variables pages; in short:

  • Footnotes are parsed and distributed to the page that hosts their anchor, but the footnote bodies are not yet drawn. Parsed, not yet renderedfootnote bodies not yet drawn — paged-renderer/src/pipeline.rs:356
  • Ruby (RubyFlag / RubyString / RubyType) is parsed and a GroupRuby MVP is drawn above the base run; per-character distribution is not yet exact. Parsed, not yet renderedGroupRuby MVP only — paged-renderer/src/pipeline.rs:4208
  • Hyperlinks inside a story (HyperlinkTextSource) are not wired through to the run, so link regions aren't emitted. Not yet parsedin-story hyperlink sources not surfaced on runs — paged-parse/src/story.rs

Frequently asked questions

How many paragraphs does a story have? Exactly as many as it has ParagraphStyleRange blocks — a new sibling block starts a new paragraph. Paragraph count is the count of those blocks, not a count of line breaks; a Br is a forced line break within a paragraph and does not start a new one.

What is the difference between a ParagraphStyleRange and a CharacterStyleRange? A ParagraphStyleRange is one paragraph and applies a paragraph style; a CharacterStyleRange sits inside it and is one run of uniform character formatting, applying a character style. A new CharacterStyleRange begins wherever the character formatting changes — bold to regular, one size to another.

Where does the literal text live? Only in Content elements, the leaves of the tree. Br and Tab are empty markers that the parser folds into the run text as \n and \t, so several Content chunks split by markers collapse into one run string with the breaks in place.

Does the renderer act on every story attribute? No. The parser reads exactly the four-level shape and the attributes documented here; other story attributes it doesn't yet act on are read past. Some constructs — footnotes, ruby, in-story hyperlinks — are parsed but not yet fully laid out, as noted above.

On this page