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?
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With