Endnotes were introduced in InDesign 13. Unlike footnotes, which are rendered in virtual containers attached to the host TextFrame, endnotes flow into extra containers of the EndnoteTextFrame class (a subclass of the TextFrame object).

However, the Endnote component is only browsable from Stories (or Tables, or Cells) that own endnote references. For example, if myStory denotes the main story of your document, myStory.endnotes collects the associated set of Endnotes. But if you select a particular reference (or some Text supposed to contain one or several endnote references), you have no access to the underlying Endnote instances. Indeed, Text.endnotes is not implemented—while Text.footnotes is.

On the other hand, because both footnote and endnote numbers are based on the special character U+0004, there seems to be no direct way to discriminate them within a sample text:

Endnote references rely on the marker U+0004, like footnote references!

It follows that the only method of extracting and parsing endnotes involves a global traversal of all objects that point to them. It's this strategy that I implemented in the $$.Dom.Endnote module of IdExtenso. In that example, the desired feature is only to get the numeral of the incoming Endnote specifier, so there's no need to dissect the remote content of the note(s). In some InDesign scripts, however, you want to tackle the problem and understand how endnotes work behind the scenes.

Endnotes versus EndnoteRanges

The Endnote component behaves like a proxy regarding text data. In this respect it resembles the Footnote object (the former being a little simpler though). Basically, from a given Endnote myEnd, you can access Text collections like myEnd.texts, .characters, .words, .insertionPoints, .textStyleRanges… Strangely, the object does not expose .paragraphs or .contents properties, or anything that involves PageItems. Perhaps the reasoning adopted by the API designers is this: in contrast to footnotes, endnotes are rendered in another place (remote textframe) and then require a specific link to a distinct entity.

That distinct entity is the object EndnoteRange, returned by the property myEnd.endnoteTextRange. The job of this component is to decouple the logical object (Endnote) from its physical realization in the layout. But surprisingly, EndnoteRanges are not the kind of things you might actually select (like Words or TextStyleRanges). Although they only occur in EndnoteTextFrames, there is no direct connection from such a frame and the endnote ranges it contains.

Interconnection between objects carrying or accessing endnotes.

So, here again, some contortion is needed to extract the information, typically using the code myEndnoteFrame.texts[0].endnoteRanges (which, by the way, tends to erroneously return an extra range that actually belongs to the next threaded frame!)

The big problem with EndnoteRanges is that they can be mixed with non-endnote text. The square brackets and shown in the below figure reflect special characters that InDesign inserts in the story to keep track of the entry/exit points of a range. Thus we can identify that the part highlighted in yellow escapes the legal content of the endnotes:

Mixture of endnote ranges and textual elements added wildly by the user.

Also, as you can see, the endnote #44 has lost its number and its paragraph break. The content of an EndnoteTextFrame is freely editable because it is, in the background, managed exactly like a traditional text container. And a very unfortunate choice was made to encode the opening and closing brackets: both rely on the very same Unicode character, U+FEFF (ZERO WIDTH NO-BREAK SPACE). Which means that you cannot analyze a fragment of text in order to extract its particular ranges, without having an exact count of all the markers that preceded it in the entire “endnote” story.

Note. — There is no actual EndnoteStory object, but you can check whether a specific Story has its isEndnoteStory property turned on.

In summary

The Endnotes collection embodies the references from the source text (the one that points to the endnotes), while the EndnoteRanges collection reflects the same data but from the perspective of the story that articulates the endnote content. A round-trip gateway makes it possible to go from an Endnote instance to the corresponding EndnoteRange, using the respective properties endnoteTextRange vs. sourceEndnote.

I haven't detailed the few properties exposed by the EndnoteRange class. They help recover start and end indices (pure integers) in the endnote story, so real InsertionPoints could be regenerated in order to make sense of the concrete text ranges. This approach works great as long as you parse the endnotes of the entire document.

But the API doesn't offer more local pointers: EndnoteRanges are not easily translatable into actual Text from a partial selection (or from an EndnoteTextFrame) while U+FEFF markers create ambiguities which complicate the task. A very similar problem arises on the side of the source text, since the markers U+0004 both denote Footnote and Endnote references.