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

Threading and overset

How one story flows across several frames in a chain, and why text that doesn't fit at the end is overset and dropped from the rendered page.

Pro· explanation

Threading is how one story flows across a chain of frames; overset is the text that still doesn't fit at the end.

In short: A story holds text and a frame shows it, so when a story is longer than its frame the text has to go somewhere. IDML's answer is threading: frames link in a chain via NextTextFrame / PreviousTextFrame, and the story is composed once then poured into them in order. Text that still doesn't fit at the end of the chain is overset — a layout outcome with no attribute of its own. This page explains how the chain is built and what our renderer does with the leftover lines.

A story holds text; a frame shows it. When a story is longer than its frame, the text has to go somewhere — IDML's answer is threading: the story continues in the next frame in a chain. The text that still doesn't fit at the end of the chain is overset.

TextFrame ANextTextFrameTextFrame B(overset if it still doesn't fit)
A single story flows through a chain of frames; NextTextFrame links one frame to the next.

A story is independent of its frames

The text lives in one Stories/Story_*.xml part regardless of how many frames display it. Frames don't contain text; they reference a story through their ParentStory attribute and contribute a box for it to flow into. So "one story in three frames" is one Story part and three TextFrame elements that all name the same ParentStory. The story is composed once, as a single continuous column, then poured into the frames in chain order.

The chain: NextTextFrame and PreviousTextFrame

Each frame in a thread points forward to the next with NextTextFrame (the Self id of the following frame) and back with PreviousTextFrame. A frame at the head of the chain has no incoming link; a frame at the tail has NextTextFrame="n" (the IDML "none" sentinel).

To build the chain for a story, our scene layer collects every frame whose ParentStory matches, picks the head — the one frame that is not named as any other frame's NextTextFrame — and follows NextTextFrame links until they run out. (PreviousTextFrame is the document's back-pointer; the walk itself only needs the forward links, and is bounded so a malformed cyclic document can't hang.) This is Document::frame_chain in paged-scene/src/lib.rs:253. The forward link is read off each frame's NextTextFrame attribute in paged-parse/src/spread.rs:1714.

The example below is exactly that: one story ustory, two frames. Frame A is too short to hold the whole story, so it names frame B as its NextTextFrame; frame B names frame A as its PreviousTextFrame; both carry ParentStory="ustory".

One story, two frames: frame A’s NextTextFrame points at frame B, which points back.

Spreads/Spread_uspread.xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<idPkg:Spread xmlns:idPkg="http://ns.adobe.com/AdobeInDesign/idml/1.0/packaging" DOMVersion="20.0">
  <Spread Self="uspread" PageCount="1" BindingLocation="0" ShowMasterItems="true" AllowPageShuffle="true" ItemTransform="1 0 0 1 0 0">
    <Page Self="upage" Name="1" AppliedMaster="umaster" ItemTransform="1 0 0 1 0 0" GeometricBounds="0 0 841.89 595.276" MasterPageTransform="1 0 0 1 0 0"/>
    <TextFrame Self="uframeA" ParentStory="ustory" PreviousTextFrame="n" NextTextFrame="uframeB" ContentType="TextType" AppliedObjectStyle="ObjectStyle/$ID/[None]" Visible="true" Name="$ID/" ItemTransform="1 0 0 1 57.638 145.8237" FillColor="Swatch/None" StrokeColor="Swatch/None" StrokeWeight="0">
      <Properties>
        <PathGeometry>
          <GeometryPathType PathOpen="false">
            <PathPointArray>
              <PathPointType Anchor="0 0" LeftDirection="0 0" RightDirection="0 0"/>
              <PathPointType Anchor="0 60" LeftDirection="0 60" RightDirection="0 60"/>
              <PathPointType Anchor="220 60" LeftDirection="220 60" RightDirection="220 60"/>
              <PathPointType Anchor="220 0" LeftDirection="220 0" RightDirection="220 0"/>
            </PathPointArray>
          </GeometryPathType>
        </PathGeometry>
      </Properties>
    </TextFrame>
    <TextFrame Self="uframeB" ParentStory="ustory" PreviousTextFrame="uframeA" NextTextFrame="n" ContentType="TextType" AppliedObjectStyle="ObjectStyle/$ID/[None]" Visible="true" Name="$ID/" ItemTransform="1 0 0 1 317.638 145.8237" FillColor="Swatch/None" StrokeColor="Swatch/None" StrokeWeight="0">
      <Properties>
        <PathGeometry>
          <GeometryPathType PathOpen="false">
            <PathPointArray>
              <PathPointType Anchor="0 0" LeftDirection="0 0" RightDirection="0 0"/>
              <PathPointType Anchor="0 400" LeftDirection="0 400" RightDirection="0 400"/>
              <PathPointType Anchor="220 400" LeftDirection="220 400" RightDirection="220 400"/>
              <PathPointType Anchor="220 0" LeftDirection="220 0" RightDirection="220 0"/>
            </PathPointArray>
          </GeometryPathType>
        </PathGeometry>
      </Properties>
    </TextFrame>
  </Spread>
</idPkg:Spread>

Overset: the text that doesn't fit

When the composed story is taller than the chain can hold, the leftover lines at the bottom of the last frame are overset. In InDesign this is the red plus sign on a frame's out-port; in the IDML there is nothing to mark it — overset is a layout outcome, not an attribute.

Our renderer composes the whole story and lays lines into the chain frame by frame. At the last frame, lines whose baseline falls past the frame's bottom are dropped rather than spilled past the frame with no clip — see paged-renderer/src/pipeline.rs:3909. That matches how InDesign's exported PDF hides overset behind the frame's clip, which keeps the rendered page faithful, but it means:

Parsed, not yet renderedoverset lines are dropped at the end of the chain, not surfaced or flagged — paged-renderer/src/pipeline.rs:3909

The text is still fully present in the story part — extraction (see extract all text) returns it all. Only the rendered page omits the lines that didn't fit. There is no diagnostic that says "this story overset"; if you need to detect it, compare the composed line count to the placed line count yourself.

Threading decides where text goes; a couple of other constructs decide whether a run is laid out at all, and these don't all behave the same:

  • Conditional text is enforced. A CharacterStyleRange with AppliedConditions is dropped when any referenced Condition resolves to Visible="false"; runs with no conditions, or whose conditions are all visible, are laid out normally. Supportedhidden conditional runs are filtered before layout — paged-renderer/src/pipeline.rs:2765
  • Hidden and note text never flows. HiddenText, Note, and index-marker subtrees are suppressed by the parser, so they never reach layout. Supportedsuppressed during story parse — paged-parse/src/story.rs:994

Both are different from overset: those runs are removed because of what they are, whereas overset text is dropped only because of where it ended up.

Frequently asked questions

What is text threading in IDML? Threading is how a single story flows across more than one frame. Each frame names the next with NextTextFrame and the previous with PreviousTextFrame, forming a chain; the story is composed once and poured into the frames in chain order.

What is overset text? Overset is the text that still doesn't fit after the last frame in the chain is full. It is a layout outcome rather than an IDML attribute — InDesign shows it as the red plus on a frame's out-port, but nothing in the IDML marks it.

Does overset text get lost when Paged renders a page? The text is never lost from the story part — extraction still returns all of it. Only the rendered page omits it: at the last frame, lines whose baseline falls past the frame's bottom are dropped rather than spilled, matching how InDesign's exported PDF clips overset. There is no "this story overset" diagnostic; to detect it, compare the composed line count to the placed line count yourself.

Do all frames that show a story contain their own copy of the text? No. The text lives in one Stories/Story_*.xml part regardless of how many frames display it; frames only reference the story through ParentStory and contribute a box for it to flow into.

On this page