• Global community
    • Language:
      • Deutsch
      • English
      • Español
      • Français
      • Português
  • 日本語コミュニティ
    Dedicated community for Japanese speakers
  • 한국 커뮤니티
    Dedicated community for Korean speakers
Exit
1

Modify width of rectangle based on page number in InDesign via Javascript

Explorer ,
Jul 06, 2013 Jul 06, 2013

Copy link to clipboard

Copied

I have a rectangle called `pageBar` on my master page !

I would like to create a script that will automatically resize the width of the rectangle based on the page number.

Something like : `width = 1280 * (pageNumber / pageTotal)`

Any help would be much appreciated.

Thanks in advance,

J.

TOPICS
Scripting

Views

3.7K

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines

correct answers 1 Correct answer

Explorer , Jul 07, 2013 Jul 07, 2013

Hi Trevor,

I did it like that...

var myDocument = app.activeDocument,

myPages = myDocument.pages,

totalPages = myPages.length,

currentPage = 1;

function getPercent(a) {

    b = (a / totalPages);

    return b;

}

for (var i=0,l=myPages.length; i<l; i++) {

        currentPage = i + 1;

        percent = getPercent (currentPage);

        $.write("Current page is : " + currentPage + " / Total page : " + totalPages + " / Percentage : " + percent + "%");   

        var myRectangle = myPages.rectangles.add({geometri

...

Votes

Translate

Translate
Contributor ,
Jul 06, 2013 Jul 06, 2013

Copy link to clipboard

Copied

What your script needs to do is walk through the document pages, look for the rectangle and essentially do the calculation you've written to size it.

To size it on any particular page, you'll need to override it to the page.

Rectangles do not have a width property. The easiest way to manage the width is to use geometricBounds or visibleBounds.

Is this document single sided? Sounds like it.

Whiich way should the width increase?

Dave

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Explorer ,
Jul 06, 2013 Jul 06, 2013

Copy link to clipboard

Copied

Hi DaveSofTypefi,

Thanks for your answer !

Yeah the document is single sided.

I would like to set the width of the rectangle like this X * (page Number / total number of pages)...

Example :  Page 1 : 100px, Page 2 : 200px, Page 3 : 300px, Page 4 : 400px. (Document of 4 pages with a width of 400px).

Does it make sense ?

Cheers,

J.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Contributor ,
Jul 06, 2013 Jul 06, 2013

Copy link to clipboard

Copied

What you say makes sense, but it doesn't answer my question: do you want it to get wider to the right, to the left or from the middle?

Are you trying to write this yourself and need guidance or are you hoping someone willl write it for you?

Dave

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Explorer ,
Jul 06, 2013 Jul 06, 2013

Copy link to clipboard

Copied

I would like to get it wider to the right !

In fact, I wanted to write this myself (as I know javascript basics) and I would like some guidance.

I find, InDesign Scripts Docs to be very confusing.

Thanks again,

J.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Guru ,
Jul 07, 2013 Jul 07, 2013

Copy link to clipboard

Copied

Hi Jacques,

This will do it.

for (var n = 1; n < 5; n++) app.activeDocument.pages[n-1].rectangles.add({fillColor: "Black", geometricBounds: [10,0, 20, (n*100) + "px" ]});

Trevor

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Explorer ,
Jul 07, 2013 Jul 07, 2013

Copy link to clipboard

Copied

Hi Trevor,

I did it like that...

var myDocument = app.activeDocument,

myPages = myDocument.pages,

totalPages = myPages.length,

currentPage = 1;

function getPercent(a) {

    b = (a / totalPages);

    return b;

}

for (var i=0,l=myPages.length; i<l; i++) {

        currentPage = i + 1;

        percent = getPercent (currentPage);

        $.write("Current page is : " + currentPage + " / Total page : " + totalPages + " / Percentage : " + percent + "%");   

        var myRectangle = myPages.rectangles.add({geometricBounds:[0, 0, 5, (800*percent)], fillColor:myDocument.colors.item("ProgressBarColor")});

}

Thanks for your input.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Guru ,
Jul 07, 2013 Jul 07, 2013

Copy link to clipboard

Copied

That's ok,

For a different approach, for speed and simplicity you could have used.

var myProgressBar = app.activeDocument.pages.everyItem().rectangles.add({fillColor: "Black", geometricBounds: [0,0,5, 1]}),

       l = myProgressBar.length, totalPages = l;

while (l--) myProgressBar.geometricBounds = [0,0,5, (l+1) * 800 / totalPages];

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Contributor ,
Jul 07, 2013 Jul 07, 2013

Copy link to clipboard

Copied

While I slept, other people have posted solutions that don't actually do what you asked (they ignore the master page item and just add new items). So let's look at the problem you actually posed. I'll note that you still haven't answered the one question I've had from the start: which way do you want the pagebar to grow, left, right or from the middle. I'll offer a solution that grows from the middle.

This is how I'd tackle the problem, starting from the outside in. Some of what I do here is not strictly necessary for a relatively simple job like this, but the principles I use are scalable so if you find yourself faced with a larger task, you can just use this approach, expanding it as necessary. My first step is to create the framework into which I'll put the details of the script (another way of saying this is, I'm going to work from the outside inwards, starting with a general statement of the problem and solving the problem by filling in the details.

This approach isn't always possible if you don't know how to address all the details. In that case, it might be necessary to start out with some more narrowly defined problem that addresses just those parts of the overall issue. Here's my first step:

//DESCRIPTION: Size PageBar on each page

(function() {

          if (app.documents.length > 0) {

                    sizePageBars(app.documents[0]);

          }

          return;

          function sizePageBars(aDoc) {

 

          }

}());

The description statement is there so that when I look at this script in the InDesign Scripts Panel at some point in the future the tool tip will tell me what the script does.

I've put the whole thing inside a single statement -- the outside parentheses -- that contains an anonymous function, i.e., one that doesn't have a name, that calls itself -- that's what the pair of parentheses after the close bracket on the last line do.

I use anonymous functions almost out of habit to protect other people's scripts from my own. In these days of multiple script engines, this is nowhere near as necessary as it was when I first stated writing scripts this way, but it does have the benefit of being able to use return to end the script.

Before doing anything to the active document, the script checks that there is one. If there isn't, it just exits silently. If I were writing this script for someone else to use, I might put and else statement in there with an alert that issued a message like "This script requires a document be opened before it can do anything." But I don't bother with messages like that when I'm writing a script for myself.

Notice that I said "active document" but in the script I used app.documents[0]. Why? Because I work for Typefi and any script I write is most likely to be used with InDesign Server which doesn't support the activeDocument property. At any moment in time with desktop InDesign, app.documents[0] is the activeDocument, so the result is the same, I end up with a variable -- the one named aDoc -- that points at the active document.

I'll post the next installment later this morning.

Dave

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
People's Champ ,
Jul 07, 2013 Jul 07, 2013

Copy link to clipboard

Copied

Well, thanks there Dave, I've learnt something new: that //DESCRIPTION

will create a tooltip. That's great!

But WHERE did you find that documented???

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Contributor ,
Jul 07, 2013 Jul 07, 2013

Copy link to clipboard

Copied

It's something I've known since I beta-tested the first version of ExtendScript so I've never had to look for the documentation. But that is a good question.

And the answer is: I can't find it.

Dave

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
People's Champ ,
Jul 07, 2013 Jul 07, 2013

Copy link to clipboard

Copied

Well, thanks for looking. And for the tip.

Ariel

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Contributor ,
Jul 07, 2013 Jul 07, 2013

Copy link to clipboard

Copied

The next step on our adventure (now that I've gotten the whole "activeDocument" thing off my chest) is to start work on the sizePageBars function. This is how I start on that:

function sizePageBars(aDoc) {

          var pageBar = getMasterItem(aDoc, "pageBar");

          if (pageBar == null) return;

          alert("Got Here.");

}

function getMasterItem(aDoc, name) {

}

Immediately, I need another function to get a reference to the master item. While aDoc is directly accessible to the getMasterItem function without having to be passed in, that's only true in this scenario. Because I like to make my functions potentially reusable in future scripts, I generally avoid taking advantage of these kinds of scoping coincidences and pass into the function as parameters anything that the function needs.

That's why I named it getMasterItem and passed in the name of the item in question rather than just calling it getPageBar and having the name be a constant in the function.

Now I'm working blind without seeing your document so I have make some assumptions. I expect that you only have one master page and there's only one item on it with the name "pageBar", but perhaps you have more than one master supporting different looks. So I'm going to allow for that.

Hmm: what does that mean? It could mean that each of your pages uses a different master and there's a pageBar on each of them, but from the way you couched your question, I don't think that's the case, so I'm not going to address that possibility, except as something to look out for as a cause of an error. This script will assume that the same master is used for all the pages. Therefore, I can get the master in question by looking at the master applied to the first page.

function getMasterItem(aDoc, name) {

          var appliedMaster = aDoc.pages[0].appliedMaster;

          var anItem = appliedMaster.rectangles.item(name);

          if (anItem == null) {

                    alert("Couldn't locate 'pageBar' rectangle.");

          }

          return anItem;

}

How did I know the page property I needed was named 'appliedMaster'? Well, actually it was a guess, but I double-checked it by looking in ESTK Object Model Viewer (see ESTK's Help menu). I looked up the Page object and checked its list of property names.

Frankly, this process is a bit like using a reverse telephone directory. If you don't already know the name of the thing you're looking for, it can take a long time to find. This is one of the reasons that scripting is not like riding a bike. There are so many names to remember that if you don't keep cycling them through your short-term memory, they're not going to stick.

MasterSpreads have a rectangles collection, and I'm assuming that when you said you'd named your pageBar you did so in the Layers panel, in which case the .item() method will find it for us. If it doesn't then you must have named it some other way -- script label? -- in which case we'll need to modify our function. In my test document, it's working so I'm getting the "Got here" alert I built into sizePageBars function.

So far so good.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Explorer ,
Jul 07, 2013 Jul 07, 2013

Copy link to clipboard

Copied

DaveSofTypefi, you right you answered properly my question ! I did ask for a script to modify an element that was already in the master page.

However, it make more sense (in my case) to create a new item rectangle on each page.

I'm new to InDesign scripting and your answers will help me in the future.

Thank you for your time.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Contributor ,
Jul 07, 2013 Jul 07, 2013

Copy link to clipboard

Copied

Took a long lunch which included escorting my young grandson on his first real train ride (from Red Bank NJ to Long Branch NJ). Let me start this afternoon by documenting a dead-end I went down. I got this far:

function sizePageBars(aDoc) {

          var pageBar = getMasterItem(aDoc, "pageBar");

          if (pageBar == null) return;

          // walk through the document's pages and deal with each page

          var theMaster = pageBar.parent;

          for (var j = aDoc.pages.length - 1; j >= 0; j--) {

                    if (theMaster == aDoc.pages.appliedMaster) { // ignore pages with other masters

                              processPage(aDoc.pages, pageBar);

                    }

          }

}

function processPage(page, pageBar) {

          // caculate desired width

 

}

when it occurred to me that in processPage I was going to need the value j in the from the loop in sizePageBars. I balked at the two immediate possibilities: pass j into the function as a third parameter or calculate the vale from the property page.documentOffset (which by definition is equal to j). The first solution just looks ugly while the second is inefficient (don't query the object model unless you absolutely have to, particularly inside a loop).

In fact, I've already broken this parenthetical rule in this script when I set the value of theMaster, but that's only done once per script and so I chose to just use pageBar.parent rather than complicate the interface to the getMasterItem function.

Before I get to my solution to this, let me comment on two things about the loop:

  1. Why did I use j as my loop-counter (rather than the universally preferred i)?
  2. Why did I count down rather than up?

I use j because in the fonts I use, it is way easier to read than i. That's it. No other reason.

I count down because:

  1. It makes no difference to the functionality (in this case).
  2. It is faster than for (var j = 0; aDoc.pages.length > j; j++) because that forces the scripting engine to re-caculate aDoc.pages.length each time around the loop.
  3. It is more compact than using two lines to write out var pLim = aDoc.pages.length; for (var j = 0; pLim > j; j++)
  4. There are some circumstances where counting down is absolutely necessary because counting up breaks references that are still to be processed (for example, when processing the results of a Find statement when the processing includes changing the found text, or when removing items from a collection, thereby changing the indexes of the remaining members of the collection).

I'll post my solution in the next message because I have to interrupt my train of thought to attend to a domestic chore.

Dave

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Contributor ,
Jul 07, 2013 Jul 07, 2013

Copy link to clipboard

Copied

LATEST

Here's the script I ended up with. I'm a bit unhappy that I ended up having the literal string "pageBar" in it twice. The second instance is there to allow the script to be run on the same document twice -- that's one of the prices you pay for using master page items in a script rather than creating objects on the fly and applying an object style to them (better than setting selected properties):

//DESCRIPTION: Size PageBar on each page

(function() {

          if (app.documents.length > 0) {

                    sizePageBars(app.documents[0]);

          }

          return;

function sizePageBars(aDoc) {

          var pageBar = getMasterItem(aDoc, "pageBar");

          if (pageBar == null) return;

          // walk through the document's pages and deal with each page

          var theMaster = pageBar.parent;

          var numPages = aDoc.pages.length;

          for (var j = numPages - 1; j >= 0; j--) {

                    if (theMaster == aDoc.pages.appliedMaster) { // ignore pages with other masters

                              processPage(aDoc.pages);

                    }

          }

          function processPage(page) {

                    // numPages, pageBar and j are visible as global variables in this function

                    // caculate desired width

                    var desiredHalfWidth = 1280 * (j + 1)/(numPages * 2); // j starts at zero;

                    // could be running script for a second time, so check to see if page already has pageBar on it

                    var bar = page.rectangles.item("pageBar");

                    if (bar == null) {

                              var bar = pageBar.override(page);

                    }

                    var pBounds = bar.geometricBounds;

                    var xCenter = (pBounds[3] + pBounds[1])/2;

                    pBounds[1] = xCenter - desiredHalfWidth;

                    pBounds[3] = xCenter + desiredHalfWidth;

                    bar.geometricBounds = pBounds;

          }

}

          function getMasterItem(aDoc, name) {

                    var appliedMaster = aDoc.pages[0].appliedMaster;

                    var anItem = appliedMaster.rectangles.item(name);

                    if (anItem == null) {

                              alert("Couldn't locate 'pageBar' rectangle.");

                    }

                    return anItem;

          }

}());

I was going to introduce to the resize method, but that would have first meant understanding it myself. The description in the object model viewer is a masterful demonstration of how not to communicate complicated information.

So I did a web search and found myself on this page by Marc Autret: http://www.indiscripts.com/post/2013/05/indesign-scripting-forum-roundup-4

Search that page for resize and you'll see what I mean. Compared to the simple manipulation of geometric bounds, using resize is a nightmare of complexity. Marc has simplified it by providing a function you can call, but I decided to just go with using the bounds.

I hope this proves helpful to you.

Dave

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines