Paragraph- and Character-Based Attributes

In InDesign, text formatting attributes are addressed at two basic levels:

— The Character Formatting Level () covers text attributes that can vary within a single paragraph, like font settings (family, style, size), kerning, language, color…

— The Paragraph Formatting Level () controls settings that affect an entire paragraph like alignment, indents, hyphenation, numbering, paragraph shading and so on.

In current InDesign versions, the ℂ and ℙ spaces have more than 150 dimensions (i.e. attributes) each.

These distinct sets of options (over 300 in total) can be fine-tuned and applied, either manually to arbitrary text portions, or in a more logical way using styles. What is not obvious to the novice is how paragraph styles and character styles actually register, share and drive these / settings.


Paragraph and Character Styles

In the Scripting DOM, a Paragraph Style (PS) is a collection of fully determined key-value pairs emanating from the and spaces. For example, a paragraph style has a fixed left indent value in as well as a fixed typeface, style and text color in .

Within a paragraph style, settings belonging to ℙ and ℂ coexist.

Note. – Due to inheritance via the basedOn property, the settings of a PS are actually determined with respect to hierarchical dependencies. However, the value of every attribute is always positively known.

By contrast, a Character Style (CS) is a collection of attributes (only), and most of them are not even specified. This structure strictly encodes differences, i.e. specific changes relative to a default, or inherited, formatting state. We'd better talk about it in terms of refinements. As long as a CS does not decide on a setting, it makes no changes to the underlying attribute and therefore remains transparent in the formatting process.

A character style is essentially an empty shell, with only a few ℂ attributes defined.

InDesign provides two locked root styles:

— The PS “[No Paragraph Style]” contains a preset of ℙ+ℂ attributes that define “the default look of text with no further formatting applied.” (Cf InDesign Plug-In Programming Guide, vol. 1, “Text Fundamentals”, Adobe, 2012, p. 289.)

— The CS “[None]” is just an abstract empty set; “it exists purely to provide a root for character styles.” (ibid.)

Note. – Do not confuse the hidden, locked “[No Paragraph Style]” item and the “[Basic Paragraph]” shown in the PS panel. Despite its reserved name, the latter can be fully edited.


About Style Overrides

Due to the very existence of styles, a text entity has two additional correlates that are not listed in the primitive and spaces, namely the underlying appliedParagraphStyle and appliedCharacterStyle.

Although any insertion point can express these two extra properties, the logical scope of a paragraph style is the Paragraph object, while a character style is intended to fine-tune TextStyleRange units, that is, any “continuous range of identical text formatting attributes.”

The “Standard Model of Text Styling”: basic ℙ+ℂ attributes are applied from a paragraph style, and some ℂ attributes can be refined thru character styles.

But the story can get complicated due to the occasional application of and/or attributes beyond the control of a style. Here is a basic scenario of such cascading events:

1. At the scale of the paragraph under consideration,
   a) the applied PS defines the expected attributes;
   b) some attributes may then be overridden (against their PS specification).

2. At the scale of the character range under consideration,
   a) the applied PS defines the expected attributes;
   b) the applied CS defines the expected refinements;
   c) some attributes may then be overridden (against PS and/or CS specs).

1b) effects are called Paragraph-Level Overrides (ℙ+ in short);
2c) effects are usually called Character Overrides (ℂ+).

InDesign can clear ℂ+ or ℙ+ separately.

Note. — “Local overrides (…) occur when a formatting change is made without modifying a style; for example, selecting a word and making it bold.” (op. cit., p. 292.)


Paragraph-Level Overrides

The ℙ+ case is the easiest to understand because it is detected at the paragraph scale and involves neither attributes nor assigned CS. In the UI, the Style Override Highlighter indicates such events by colored boxes bordering the paragraph(s) in question. For example, here is the result of manually altering the alignment of two paragraphs from their regular centered state:

Turning on the Style Override Highlighter…

The figure above illustrates an important fact in that it does not reveal any ℂ+ issue while mixed attributes are visible with the same applied style “MyParaStyle”. The formatting of the first paragraph fully complies with all attributes defined in the PS — since the “[None]” style is uniformly assigned to that line. Yet, in the second paragraph, a custom CS style “ItalStyle” is applied, with the effect of italicizing the text. This corresponds to refinement step 2b) and therefore does not result in any character overrides.

By definition, ℙ+ events only report discrepancies in paragraph formatting. They don't care about attributes. With multiple CS applied throughout the same paragraph, still no ℂ+ to deplore:

Paragraph-level overrides don't care about <code>ℂ</code> events.

In the figure, the bottom paragraph was restored in relation to its PS using a code like this:

// Clearing ℙ+
 
para.clearOverrides(OverrideType.PARAGRAPH_ONLY);
 

But the command ...clearOverrides(OverrideType.ALL) would have had exactly the same effect since there is no ℂ+ in our example. For the moment, remember the following translations concerning the OverrideType enumeration:

   OverrideType.PARAGRAPH_ONLY  ↔  ℙ+
   OverrideType.CHARACTER_ONLY  ↔  ℂ+
   OverrideType.ALL  ↔  ℙ+ and ℂ+

Note also that clearing ℙ+ inherently affects the entire paragraph. So you could just as easily run the command from a simple insertion point with the same outcome:

// Clearing ℙ+ (...from any paragraph subtext)
 
para.insertionPoints[0].clearOverrides(OverrideType.PARAGRAPH_ONLY);
 

Let's now move on to the reciprocal question: How to detect that a paragraph is altered by some ℙ+? (I mean, specifically altered at the PARAGRAPH_ONLY level, no matter what happens in the space.)

To my knowledge, you cannot get this information directly from the scripting DOM! Neither of the two modes of investigation available to us, myText.styleOverridden and myText.textHasOverrides(...), allows us to discern ℙ+ issues from ℂ+ issues. One might think the opposite by examining the arguments offered by the method textHasOverrides() but it turns out that those arguments only handle the PS/CS distinction (via the StyleType enum), not the ℙ+/ℂ+ distinction encoded in OverrideType.

If we return to our scenario, this means that these features cannot discern
   1b) cases, i.e. ℙ+ against PS
from
   2c) cases, i.e. ℂ+ against PS and/or CS.

The Scripting DOM only tells us, at best, against which StyleType (PS or CS) the override appears. So in the happy circumstance where you can strictly establish a CS violation, you know you are dealing with a ℂ+ because such an event does not occur in 1b). But in the case of a PS violation, you don't know if you are facing a 1b) or 2c) issue, that is, a ℙ+ or a ℂ+ override.

Note. — InDesign 12 introduced a second argument in textHasOverrides(...), the Boolean parameter charStyleAsOverride, which does not solve our problem in any way. Worse, it tends to obscure the ℙ+/ℂ+ dichotomy. (Coming back to it a little further…)


The Case of Character Overrides

Remember the three sub-steps that weigh in on processing attributes:
   2a) desired settings from the applied PS;
   2b) desired refinements from the applied CS;
   2c) ℂ+ overrides (against PS and/or CS).

Here is a typical example of 2c) override as shown by the Highlighter:

A typical character override: Bold manually applied to a word.

Unlike the ItalStyle and RedStyle text ranges, which exhibit 2b) refinements as dictated by the corresponding character styles, the BoldOver part is marked as a ℂ+ (blue highlighting). However, note that the ‘+’ suffix is only displayed in the paragraph styles panel (“MyParaStyle+”.) This should not be interpreted as a ℙ+ issue though.

Indeed, InDesign is just sending us the following message: Regarding attributes, this local bolding violates the PS specifications AND is not supported by the active CS — the underlying style being “[None]” in that particular area. Please note that this ℂ+ does not conflict with the assigned CS at this point, because the “[None]” style (i.e. the empty shell) demands nothing regarding the font, the typeface or any attribute, neither positively nor negatively. Hence the local override occurs with respect to the PS only.

Still with me? Ok, let's look at a more striking example. Suppose we change the ItalStyle part to purple. What do you expect now from the respective style panels?

Still the same!

Surprised? Except for highlighting a new style range, there is no difference in the UI. InDesign keeps sending us the very same signal: Regarding attributes, this purple assignment violates the PS specifications AND is not supported by the active CS. Please note that it does not conflict with the “ItalStyle” rules, which only define a +Italic refinement and don't care about the text color!

This is a very important example to keep in mind. We are in a region where a character override (ℂ+) coincides with the scope of a non-neutral CS, and yet the conflict does not concern this applied CS at all, but still the overhanging PS.

Now, a definitive experiment is to apply the purple color to the RedStyle part:

Finally we have both a PS and a CS violation with this character override!

This time, the fillColor=Red criterion is violated, so we have a double ℂ+ marked by two trailing ‘+’ symbols in the respective style panels: “MyParaStyle+” (PS scope) and “RedStyle+” (CS scope).

We can even go further. Let's manually change that purple override and apply a [Black] color instead, that is, the fillColor attribute that happens to be the one “MyParaStyle” expects. Here is the result:

A CS violation that no longer violates the PS specification.

Note that “MyParaStyle” is then displayed without the ‘+’ suffix in the PS panel. The ℂ+ highlighting is maintained anyway, which may seem counterintuitive since the attributes now fully match the PS specification!

What the InDesign panels tell us now is this: Although the text range fully complies with the PS settings (no conflict in that panel), it still contradicts the refinements requested by “RedStyle”, i.e. the applied CS.


Interlude: Going Back to [None]

You might naively think that by forcefully applying the “[None]” style to a ℂ+ issue that only concerns a CS, the override will vanish and everything will return to normal. But if we do this test on the ItalStyle portion once ‘restored’ to Regular, we get a result for which nothing had prepared us:

Why does the Highlighter still signal a character override in such a case?

The question is WHY? Why does InDesign keep reporting a ℂ+ in a region where all and settings perfectly match the applied PS (“MyParaStyle”) and can in no way be objected to by the “[None]” style?

I stood there for a long time wondering what was going on there… To top it off, if one selects the disputed part and run the following code,

const PS_TYPE = StyleType.PARAGRAPH_STYLE_TYPE;
const CS_TYPE = StyleType.CHARACTER_STYLE_TYPE;
 
sel = app.selection[0]; // The ℂ+ violation is selected
 
alert( sel.textHasOverrides( PS_TYPE ) ); // true!!!
alert( sel.textHasOverrides( CS_TYPE ) ); // true!!!
 

everything happens as if the scripting subsystem detected a double departure from the PS and CS settings! Since there is definitely no ℙ+ in my example, this confirms forever that no analogy can be drawn between StyleType.PARAGRAPH_STYLE_TYPE and OverrideType.PARAGRAPH_ONLY. As we said above, these two enumerators have absolutely no connection.

Hoping to get a hold of the cause of the override, you may want to check by yourself if the selection and the underlying style(s) actually coincide with respect to the appliedFont, the fontStyle or any other properties you might suspect. E. g.

sel = app.selection[0];
 
ps = sel.appliedParagraphStyle;
alert( ps.name ); // MyParaStyle
 
cs = sel.appliedCharacterStyle;
alert( cs.name ); // [None]
 
alert( sel.appliedFont===ps.appliedFont ); // true :-)
alert( sel.fontStyle===ps.fontStyle );     // true :-)
// etc
 

So, everything perfectly matches. Even deeper diff routines won't discover why InDesign sees a gap where there isn't one.

Here's our last resort: select the so-called character override, create a fresh CS from there, apply that very style — the ℂ+ highlighting disappears! — and examine how its settings differ from the root style. (Click on the animation to see the ending in cinemascope!)

Prepare for a revelation!

How to interpret this result? By some means that is not apparent within DOM properties, InDesign just remembers that a local +Regular command has been manually applied to the text range. The resulting state is formally indistinguishable from that specified by the applied PS (“MyParaStyle”), but the application still sees it as a “pure local ℂ+ override” being added during step 2c). Whether this should be considered a bug, I don't know. In any case, if we export the document in IDML from its ℂ+ state, the unwanted highlighting disappears upon reopening and our sel.textHasOverrides(…) tests now return false. We can therefore speak of a sort of ghost print of a style override that no longer exists.

Getting rid of this dust is not difficult. After all, it would be enough to delete the newly created CS (after the step shown in the animation); just make sure you deactivate the Preserve Formatting option:

Do not preserve the (ghost) formatting! If you did this, the fake override would instantly reappear.

A more expeditious approach for developers is to run the command sel.clearOverrides(OverrideType.CHARACTER_ONLY) on the ℂ+. Ironically this command is not available in the interface when you select the problematic text,

Although unavailable in the UI, the Clear Overrides option can be successfully executed from your script :-)

but the code does work and eliminates the issue. Anyway the real problem is not cleaning up those ghost overrides, but detecting them in the first place! If you need an advanced style override processing script, you'll likely have to implement your own comparison or “style-diff” algorithm. Unfortunately, your efforts will still not be enough to detect fake ℂ+ that the Highlighter claims to identify.

Note. - And I'm not even talking about nested styles, nested GREP styles and similar side effects. (It would take another long article to cover this subject.)


Related Scripting Tools

It's high time to say more about the scripting features in contact with style overrides.

1. clearOverrides(OverrideType)

The Text.clearOverrides method, available since InDesign 4.0.

The first historic DOM method, Text.clearOverrides(...), was born with InDesign CS2 (4.0) and remains of prime importance today. It introduces the OverrideType enum already mentioned with its three options (.CHARACTER_ONLY, .PARAGRAPH_ONLY, .ALL) and can selectively remove ℂ+ and/or ℙ+ overrides from any Text entity. It is also supported at the Story level.

2. styleOverridden: Boolean

The Text.styleOverridden property, available since InDesign 5.0.

The Boolean property Text.styleOverridden (also exposed to Story objects) was added in InDesign CS3 (5.0). The documentation is slightly ambiguous about it: its exact purpose is to indicate whether the target text contains one or more overrides (regardless of their type) with respect to the applied paragraph style. In other words, this property reliably reflects the ‘+’ suffix visible in the PS panel; it addresses ℂ+ and/or ℙ+ events.

mySelection.styleOverridden will wake up in almost all relevant cases, that is, situations represented by
   “MyParaStyle+ ¦ MyCharStyle”   or   “MyParaStyle+ ¦ MyCharStyle+
in the style panels. As we have seen, alternate cases of the form
   “MyParaStyle ¦ MyCharStyle+
are both bug harbingers (see previous section) and quite rare in practice. Remember: they correspond to a ℂ+ that contradicts the applied CS while restoring attributes in line with the PS.

As a general tip, only invoke styleOverriden (or the methods described below) from a uniform TextStyleRange. When investigating overrides, start by splitting your target into homogeneous parts — myText.textStyleRanges.etc — so as to extract clear information that does not mix multiple formatting issues. Failing such rules, you would only get properties from the first style range identified in the text, which would completely distort your conclusions.

3a. textHasOverrides(StyleType): Boolean

First version of Text.textHasOverrides(), InDesign CC 11.

As Uwe Laubender noted in this 2019 thread, Text.textHasOverrides(...) was born with a single argument (the newly created StyleType enumerator) in InDesign 11.x. This innovation seemed like excellent news, but rather than bringing the refinements that one might expect, it mainly introduced major confusion.

Text.textHasOverrides(StyleType) claims to distinguish overrides in conflict with the applied PS (PARAGRAPH_STYLE_TYPE) from those in conflict with the applied CS (CHARACTER_STYLE_TYPE). At first glance, the PS option is redundant with Text.styleOverridden, but it introduces a sneaky nuance: By default, the very detection of a character style — I mean, not “[None]” — will be reported as an override, regardless of whether the formatting attributes perfectly match the CS rules.

So, with this new tool in hands, you still don't know if you are dealing with ℂ+ or ℙ+ issues, but you take the risk of finding so-called overrides that just don't exist…

3b. textHasOverrides(StyleType,Boolean): Boolean

Current version of Text.textHasOverrides(), from InDesign CC 12.

Finally, the current version of Text.textHasOverrides(...) (which dates back to InDesign 12.x) provides an optional extra argument, charStyleAsOverride (Boolean), which in some way repents of decisions previously taken. By explicitly setting this parameter to false, you give up on “considering character styles as overrides”.

Logically, this second parameter only makes sense if the first is set to StyleType.PARAGRAPH_STYLE_TYPE. I haven't found any situation where it affects the returned value when StyleType.CHARACTER_STYLE_TYPE is used.

(In the following, I will abbreviate StyleType.PARAGRAPH_STYLE_TYPE to <PS> and StyleType.CHARACTER_STYLE_TYPE to <CS>.)

In practice, the test
   (a) x.textHasOverrides(<PS>,false)
is almost always equivalent to
   (b) x.styleOverridden.

The only case I found where (a) answers true while (b) answers false is that of a ℂ+ in conflict with the applied CS but in agreement with the applied PS, i.e. “MyParaStyle ¦ MyCharStyle+” (our rare case.) The behavior of textHasOverrides(<PS>,false) seems quite absurd in that context. In addition, we can't get anything out of that code as soon as a ℙ+ complicates the matter. (The problem has not changed in this regard, there is nothing to detect ℙ+ issues alone.)

Now, what is the purpose of x.textHasOverrides(<CS>)? From my experiments, true informs us that there is a ℂ+ at some point, not necessarily with respect to the applied CS (!) but let's not sulk, this still remains valuable information.

The map below summarizes the results of various tests carried out with these methods. The “signature” attached to any analyzed text part shows how the scripting features react to each situation. (Click the image to see the detailed codes.)

Scripting tool responses for different kinds of overrides.