Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

inDesign JSX scripted add of heading and content into textFrame

I'm attempting to use inDesign JSX scripts to insert the following data into a document:

data = [{heading:"Heading 1", content: ["Some content"]},
 {heading:"Heading 2", content: ["Some other content with", "Multiple paragraphs"]}]

The data has to be placed into a single TextFrame, but have different styling on the heading and content.

The only way I can see to add the text is in one go via the textFrame.contents variable:

allContent = "";
headingParagraphs = []; // keep track of which paragraphs are headings
paragraph = 0;
for (var i = 0; i < data.length; i++) {
  allContent += data.heading + "\r"; // Use a newline to split the paragraph
  headingParagraphs.push(paragraph);
  paragraph++;
  for (var j = 0; j < data.content.length; j++) {
    allContent += data.content[j] + "\r"; // Use a newline to split the paragraph
    paragraph++;
  }
}
textFrame.contents = allContent; // all data is in, but all text is styled the same

Then once the data is in, I iterate the paragraphs and add some style to the headings:

for (var i = 0; i < textFrame.paragraphs.count(); i++) {
  if (headingParagraphs.indexOf(i) != -1) { // this is a heading paragraph
    textFrame.paragraphs[i].pointSize = 20;
  }
}

This works fine for small data sets that fit on one page, but once the contents gets bigger than the frame, paragraphs only returns visible paragraphs. And if I follow on to a new textFrame, paragraphs get split and the headingParagraphs[] array no longer lines up.

Ideally I'd like to append to the contents and set styles before I append the next content - but the API docs aren't very clear on how you might do that (if at all)

// Pseudo code:
for all sections:
  append the heading to the frame, split to next page if needed
  style all the *new* paragraphs as headings
  for all section contents
    append the content to the frame, split to next page if needed
    style any *new* paragraphs as normal content

Is there a way to achieve this using either an append function or some other way to assign headings to the right place after content has been added? Perhaps special characters in the content to define style?

like image 571
Josh B Avatar asked Jun 02 '14 00:06

Josh B


1 Answers

Your longer text gets messed up because currently you are working inside a single text frame. As soon as the text runs out of this one frame, you can't refer to them as this frame's "owned" paragraphs anymore. Use parentStory instead, as it points to the whole story, inside one text frame or spanning more than one. It also keeps on working if the text gets overset.

So if you have a starting frame called textFrame, set a new variable story to textFrame.parentStory and use that to add text.

As for adding text to this frame(/story): indeed, there is no fast way to add formatted text. Setting contents only works for long swathes with the same formatting. One way I've used is to write INX formatted text to a temporary file and importing that. It's slow for short fragments, but larger stories (up to several hundreds of pages) can be created very efficiently in Javascript itself, and then importing it into ID is .. well, it aint fast but faster than trying to do it "manually".

The other way is to add contents one paragraph at a time. The trick is to set formatting and add your text to story.insertionPoints[-1]. This, in a particularly handy notation, refers to the very last text insertion point of the story. You can think of an insertion point as "the text cursor"; you can 'apply' formatting to it, and any text added will then have this formatting as well.

Your code snippet reworked to add one data item at a time:

for (var i = 0; i < data.length; i++)
{
    story.insertionPoints[-1].pointSize = 20;
    story.insertionPoints[-1].contents = data[i].heading + "\r"; // Use a newline to split the paragraph
    story.insertionPoints[-1].pointSize = 10;
    for (var j = 0; j < data[i].content.length; j++)
    {
        story.insertionPoints[-1].contents = data[i].content[j] + "\r"; // Use a newline to split the paragraph
    }
}

One thing to note is that you cannot temporarily override the pointSize here. If you set it to your larger size, you must also set it back to the original size again (the '10' in my snippet).

Can I convince you to look in to using paragraph styles? With paragraph styles, you'd have something like

hdrStyle = app.activeDocument.paragraphStyles.item("Header");
textStyle = app.activeDocument.paragraphStyles.item("Text");

for (var i = 0; i < data.length; i++)
{
    story.insertionPoints[-1].contents = data[i].heading + "\r"; // Use a newline to split the paragraph
    story.insertionPoints[-2].appliedParagraphStyle = hdrStyle;
    for (var j = 0; j < data[i].content.length; j++)
    {
        story.insertionPoints[-1].contents = data[i].content[j] + "\r"; // Use a newline to split the paragraph
        story.insertionPoints[-2].appliedParagraphStyle = textStyle;
    }
}

Note that it's worth here to invert inserting contents and applying formatting. This is so any previous 'temporary' formatting gets cleared; applying a paragraph style this way overrides any and all local overrides. As you have to apply the style to the previous paragraph (the one before the hard return you just inserted), you would use insertionPoints[-2] here.

The advantages of using styles over local formatting are countless. You can apply all formatting you want with a single command, safely remove all local overridden formatting, and change any part of the formatting globally if you are not satisfied with it, rather than having to re-run your script with slightly different settings.

like image 59
Jongware Avatar answered Dec 22 '22 01:12

Jongware