RichEdit Paragraph Indents

RichEdit paragraph indents are based on the original RichEdit 1.0 definitions. These differ somewhat from Microsoft Word’s definitions and hence from TOM’s (RichEdit’s Text Object Model), which were developed in collaboration with the Word team. In addition, the RTF file format uses Word’s definitions, naturally. RichEdit’s paragraph indents can be set using the EM_SETPARAFORMAT message and retrieved using EM_GETPARAFORMAT both of which use either the PARAFORMAT or PARAFORMAT2 structures. The differences in these definitions have led to considerable confusion over the years, so this post is dedicated to explaining the relationships. These definitions differ, in turn, from those used in math paragraphs, as discussed in recent blog posts.

The PARAFORMAT[2] structure has the indent variables

                LONG dxStartIndent;

LONG dxOffset;

                LONG dxRightIndent;

The PARAFORMAT2 structure also contains the variable

                WORD wNumberingTab;

Meanwhile Word, TOM, and RTF have the variables (in TOM terminology) FirstLineIndent, LeftIndent, RightIndent, and ListTab. They look similar to the corresponding PARAFORMAT2 variables, but the first two have different meanings and hence the confusion, since RichEdit clients typically end up using a mixture of the APIs.

First consider cases having a zero value of wNumberingTab, which equals the TOM ListTab. Then the first line is indented by LeftIndent + FirstLineIndent. Meanwhile in PARAFORMAT notation, the first line is indented by PARAFORMAT::dxStartIndent. Subsequent lines in the paragraph are indented by the TOM LeftIndent, while in PARAFORMAT notation, they are indented by PARAFORMAT::dxStartIndent + PARAFORMAT::dxOffset. Summarizing the TOM/PARAFORMAT relationships, we have

TOM/Word

PARAFORMAT

FirstLineIndent

−dxOffset

LeftIndent + FirstLineIndent

dxStartIndent

LeftIndent

dxStartIndent + dxOffset

ListTab

wNumberingTab

RightIndent

dxRightIndent

Here the third relationship follows from the first two, but it’s included for clarity.

Now consider cases with nonzero wNumberingTab or equivalently ListTab. This value is the width reserved for the list (numbering) text on the first line of a numbered/bulleted paragraph provided its value exceeds both dxOffset (−FirstLineIndent) and the width of the list text itself. In particular, if ListTab < −FirstLineIndent, ListTab has no effect. Since FirstLineIndent is negative in most scenarios (e.g., when you type the Ctrl+Shift+L hot key to turn on a bulleted list), FirstLineIndent often determines the space allocated to the numbering text instead of ListTab. But for a positive FirstLineIndent, ListTab determines the space so long as it’s wider than the numbering text. Summarizing these relationships, the start of text on the numbered line is indented by (using PARAFORMAT2 variables)

                dxStartIndent + max(list text width, wNumberingTab, dxOffset)

or equivalently with TOM variables

                LeftIndent + FirstLineIndent + max(list text width, ListTab, −FirstLineIndent)

RTF control words for these variables are \fiN (FirstLineIndent), \liN (LeftIndent), \riN (RightIndent), and \pnindentN (ListTab). Here \pnindent is a control word in the original Word list notation. Later control words are described in the RTF specification under the heading “List Tables”.