Faster way to edit style ranges of text

Hi everyone!

I’m currently working on an InDesign plugin that applies styles (mostly character styles) to texts in InDesign using a UXP plugin. For simplicity, let’s say this was on a per-story basis, i.e., I have a function applyStyles(story: Story) that applies the character styles to specific sections of the given story.

The best way I found to apply the styles was by using something like

story.characters
     .itemByRange(start, stop)
     .applyCharacterStyle(...)

for every range start-stop (or, for example, the same with story.words). While it isn’t the actual project (which is under NDA, so I can’t talk about it), to give you a less abstract idea, the problem is comparable to something like syntax highlighting some code.

While this works, it is unfortunately quite slow (I’m dealing with >> 500 ranges to highlight). And the bottleneck then becomes the imperative DOM interactions.

While I’m not hopeful (as I’ve already searched for this for quite some time), I still wanted to ask if there is some more “declarative” API to batch-apply style ranges to a story. I.e., something with which, instead of applying every style change piece by piece (each with costly DOM operations), I could just, in one DOM interaction, “replace” the story.textStyleRanges with the desired result?

While I fear to know the answer already, maybe some InDesign wizard from this community will be able to make my day. I at least wanted to ask.

So thank you very much in advance
Zuri

Hello Zuri,

Is the itemByRange part critical to your process? The reason I ask is that for over a decade I’ve been applying styles to text using the changeText() method in ExtendScript. I’ve not needed to test it out in UXP, but that will happen eventually. I don’t know if that would be faster, but if you haven’t tried it, it would give you an option to compare speed.

But, as far as I know, you can’t do what you want directly with the text style ranges. If that proves wrong, please share!

Jon

1 Like

Hi Jon,

Thanks for your quick response! I’d love to give changeText() a shot, but have to admit that I have no idea how it should/could be used. The documentation, unfortunately, is … not very clear:

Text changeText(Boolean reverseOrder )
Finds text that matches the find what value and replaces the text with the change to value.
Parameters
Type Name Description
Boolean reverseOrder If true, returns the results in reverse order. (Optional)

Do you, by any chance, have a sample of how this would have worked in ExtendScript or could point me into the right direction in trying to make sense of what this method is supposed to do?

Thanks in advance!
Zuri

If possible, use GREP style to achieve your goal without writing scripts.

If that is not enough, Tagged-text or XML tags may be used. These map content to InDesign styles by representing plain text in a specific format. Perhaps something like ReactDomServer.renderToString could be used to write more declaratively.

If that doesn’t work, use a search-and-replace method such as changeText(). It is still a procedural way of writing, but you can expect to speed up the process since it is not a DOM operation.

2 Likes

You basically invoke InDesign’s Find/Change window. You might want to use the GREP panel rather than the text panel. You mention something like syntax highlighting, for which grep replacement is much more useful than than text replacement.

For example, to apply character style X to instances of for, while, repeat, until, use this code:

app.findGrepPreferences = app.changeGrepPreferences = null;
app.findGrepPreferences.findWhat = /\b(for|while|repeat|until)/;
app.changeGrepPreferences.appliedCharacterStyle =
app.activeDocument.characterStyles.item(‘X’);
myStory.changeGrep();

You could do this with GREP styles (obviating the need for a plug-in/script!) as sttk3 mentioned, but in large documents GREP styles can slow the document down considerably.

4 Likes

Come to think of it, I used to generate the necessary resulting TextStyleRanges on my own and apply them in the case of Adobe XD and Photoshop. The same approach may be possible in InDesign. But I’ve never actually done it.

1 Like

That was my first idea, too (coming from Adobe XD Plugin Development). But textStyleRanges is readonly and I haven’t seen any method to “get around that”, so to speak.

I’ll try it with the changeText for now (this might be able to solve at least part of my problem, so it might already be enough).

Thanks, everyone! :blush:

1 Like

Using changeText (and/or changeGrep) worked like a charm – thank you so much, everyone! I’ve marked @PeterKahrel’s response as the solution as it is (with the sample code) probably the most straight-forward answer to look up in a few months from now, but thank you so much to everyone for your input and help – this honestly saved the project :blush:.

Thanks again!

1 Like

Or let’s do this instead: here’s the function I ended up using, based on the various answers:

export function replaceCharacterStyle(
  initial: CharacterStyle,
  target: CharacterStyle,
  story: Story,
) {
  // @ts-expect-error Reset – types are messed up, but that's just the InDesign API
  app.findGrepPreferences = app.changeGrepPreferences = null;

  app.findGrepPreferences.appliedCharacterStyle = initial;
  app.changeGrepPreferences.appliedCharacterStyle = target;
  story.changeGrep(false);
}
1 Like