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

Progress/Current page bar script - running vertically

Community Beginner ,
Jul 22, 2024 Jul 22, 2024

Copy link to clipboard

Copied

Hi all,

 

I'm trying to have a script which creates a 'document progression bar' on pages within a document, and the bars height gets larger as the reader goes down the pages. Scoured the internet, done my googling - there's a few solutions where people have written scripts which create a progress bar, however all seem to be an increase in width whereas I'm looking for height.

 

The best one that I came across was from Arno Richter which creates everything from the styles and swatch, to drawing the bar from a user set position, width, and height. Honestly a great script.

https://oelna.de/blog/779

https://oelna.de/blog/wp-photos/2012-07-05/progressbar.jsx

 

The function for this creates a bar increasing in width from left to right, whereas I'm trying to figure out how to change this to go from top to bottom. 

 

Forgive me my javascript knowledge is limited to pretty much none so I am very stumped. 

 

Any help would be greatly appreciated!

 

Thanks,

Tom

TOPICS
Scripting

Views

267

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 2 Correct answers

Community Expert , Jul 22, 2024 Jul 22, 2024

We can't see the rest of the code, the problem seems to be with the doScript() call.

 

But anyway, here is a variant of a script I did a while ago. It's fairly simple. In your document, add the full-height progress bar(s) on the master page(s), and label them progress bar on the Layers panel(see the sample here: progress-bar.indd).

 

With this set-up the script needn't bother with object styles and there's less arithmatic to do.

When you add or remove pages, simply run the script again.

 

 

page
...

Votes

Translate

Translate
Community Expert , Jul 23, 2024 Jul 23, 2024

@Thom_D., you've already had some good help, and @Peter Kahrel's script is excellent and very clever.

 

I just thought this would be a fun scripting exercise, so I've written something too, taking a different approach, and so I'll post it here too in case it suits different situations. My idea was to put a shape, eg. a rectangle on the start and end pages, give them names "@start" and "@end" so the script can find them, and interpolate them on the intermediate pages. I think set up is very simpl

...

Votes

Translate

Translate
Community Expert ,
Jul 22, 2024 Jul 22, 2024

Copy link to clipboard

Copied

I'm on my phone so can't give you line numbers - but you have 3x lines that start with:

 

bounds[3] = bounds[1]+

 

You need to change "3" to "2", "1" to "0" and "progress_width" to "progress_height".

 

Then you need to change values in the dialog - when you run script. 

 

 

This one - twice:

bounds[3] = bounds[1]+progress_width;

to:

bounds[2] = bounds[0]+progress_height;

 

And this - once:

bounds[3] = bounds[1]+Math.round((progress_width/(pages_total-1))*i*100)/100;

 to:

bounds[2] = bounds[0]+Math.round((progress_height/(pages_total-1))*i*100)/100;

 

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
Community Beginner ,
Jul 22, 2024 Jul 22, 2024

Copy link to clipboard

Copied

Hey Robert, 

Thanks for looking into it! I made the changes you suggested and I'm getting an javascript error appear.

 

The code I changed:

var bounds = new Array();
	bounds[0] = progress_top; //margin-top
	bounds[1] = page_width-progress_width-progress_side; //margin-right
	bounds[2] = bounds[0]+progress_height; //height
	
	var progress_backgrounds = new Array();
	var progress_fills = new Array();
	var progress_outlines = new Array();
	for(var i=0; i<pages_total; i++) {
	
		//draw the background, if requested
		if(progress_background == true) {
			bounds[2] = bounds[0]+progress_height;
			progress_backgrounds[i] = app.activeDocument.pages[progress_start-1+i].rectangles.add({
				geometricBounds: bounds,
				appliedObjectStyle: app.activeDocument.objectStyles.itemByName('Progress Bar Background')
			});
		}
	
		//draw the actual bar
		//don't fill the first page progress bar. looks better.
		if(i>0) {
			bounds[2] = bounds[0]+Math.round((progress_height/(pages_total-1))*i*100)/100;
	
			//indesign pages start at 0, so add 1 to compensate when selecting from the array
			progress_fills[i] = app.activeDocument.pages[progress_start-1+i].rectangles.add({
				geometricBounds: bounds, 
				appliedObjectStyle: app.activeDocument.objectStyles.itemByName('Progress Bar Fill'),
			});
	
			debuglog('page '+(progress_start+i)+' (index '+i+') height: '+(bounds[3]-bounds[2]));
		} else {
			debuglog('page '+(progress_start)+' (index '+i+') height: 0');
			progress_fills[0] = null;
		}
	
		//draw the outline, if requested
		if(progress_outline == true) {
			bounds[2] = bounds[0]+progress_height;
			progress_outlines[i] = app.activeDocument.pages[progress_start-1+i].rectangles.add({
				geometricBounds: bounds,
				appliedObjectStyle: app.activeDocument.objectStyles.itemByName('Progress Bar Outline')

 

 

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
Community Expert ,
Jul 22, 2024 Jul 22, 2024

Copy link to clipboard

Copied

We can't see the rest of the code, the problem seems to be with the doScript() call.

 

But anyway, here is a variant of a script I did a while ago. It's fairly simple. In your document, add the full-height progress bar(s) on the master page(s), and label them progress bar on the Layers panel(see the sample here: progress-bar.indd).

 

With this set-up the script needn't bother with object styles and there's less arithmatic to do.

When you add or remove pages, simply run the script again.

 

 

pages = app.activeDocument.pages.everyItem().getElements();

function getBar (page) {
  var bar = page.pageItems.item('progress bar');
  if (bar.isValid) {
    return bar;
  }
  var m = page.masterPageItems;
  for (var i = 0; i < m.length; i++) {
    if (m[i].name === 'progress bar') {
      return m[i].override (page);
    }
  }
  return null;
}
    

b = pages[0].appliedMaster.pageItems.item('progress bar');
if (!b.isValid) {
  alert ('Bar not found');
  exit();
}

h = (b.geometricBounds[2] - b.geometricBounds[0]) / (pages.length);

for (i = 0; i < pages.length; i++) {
  pbar = getBar (pages[i]);
  if (pbar) {
    gb = pbar.geometricBounds;
    pbar.geometricBounds = [gb[0], gb[1], gb[0] + (h*(i+1)), gb[3]];
  }
}

 

 

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
Community Expert ,
Jul 23, 2024 Jul 23, 2024

Copy link to clipboard

Copied

LATEST

@Thom_D., you've already had some good help, and @Peter Kahrel's script is excellent and very clever.

 

I just thought this would be a fun scripting exercise, so I've written something too, taking a different approach, and so I'll post it here too in case it suits different situations. My idea was to put a shape, eg. a rectangle on the start and end pages, give them names "@start" and "@end" so the script can find them, and interpolate them on the intermediate pages. I think set up is very simple (see attached indesign demo document and instructions are in the script).

 

If you try it out, let me know how it goes.

- Mark

 

/**
 * @file Update Page Progress Graph.js
 *
 * Updates a "Page Progress Bar", meaning a graph drawn using Indesign page items
 * where one element, eg. a rectangle, grows as the page index increases, to show
 * the reader's progress within a document.
 *
 * Usage:
 *    1. Create a path item named `@start` on the first page of the page progress bar.
 *    2. Create a path item named `@end` on the last page of the page progress bar.
 *    3. Run script, which will create (or update) one shape on each intervening page
 *       such that is interpolated between `@start` and `@end`.
 *
 * Rules:
 *    - `@start` and `@end` page items must have paths, and the same number of path points.
 *    - The interpolation is similar to Illustrator blends, so `@start` and `@end` items'
 *      path points are expected to match up, point 0 to point 0 etc.
 *    - `@start`, `@mid` and `@end` page items must be on a normal page, not on a master page.
 *
 * Tips:
 *    - You can create any number of static elements to visually support the page progress bar,
 *      such as a background box or border. These are not used by the script.
 *    - The simplest `@start` and `@end` page items are rectangles. Just draw one for the start,
 *      name it `@start` then copy/paste it to the end page, rename it to `@end` and adjust the
 *      points to suit, eg scale it to the maximum size of the bar.
 *    - Because the script interpolates between `@start` and `@end` paths, you can make your
 *      progress bar go horizontally or vertically simply by the relationship between the paths.
 *    - If you don't want the progress bars on every page, then run script once, and manually
 *      remove the bars you don't want, and thereafter run script with `createNewBars: false`
 *      (script will now update bars that exist, and won't create new bars if they are missing.)
 *
 * @author m1b
 * @discussion https://community.adobe.com/t5/indesign-discussions/progress-current-page-bar-script-running-vertically/m-p/14751998
 */
function main() {

    if (0 === app.documents.length)
        return alert('Please open a document and try again.');

    updatePageProgressBar({
        doc: app.activeDocument,
        createNewBars: true,
    })

}
app.doScript(main, ScriptLanguage.JAVASCRIPT, undefined, UndoModes.ENTIRE_SCRIPT, 'Update Page Progress Graph');


/**
 * Updates a Page Progress Bar, by interpolating
 * between a `@start` item and an `@end` item
 * across the document's pages.
 *
 * Tips:
 *   - supply different start/mid/end names if there
 *     are multiple progress bars in the document.
 *   - set `createNewBars` to false if you have deliberately
 *     removed `mid` items from your document. This way you
 *     can update only those pages that have the `mid` items.
 *
 * @author m1b
 * @version 2024-07-23
 * @param {Object} options
 * @param {Document} options.doc - an Indesign Document.
 * @param {Boolean} [options.createNewBars] - whether to create a new bar on each intervening page, if missing (default: true).
 * @param {String} [options.startName] - the start item's name (default: '@start').
 * @param {String} [options.endName] - the end item's name (default: '@end').
 * @param {String} [options.midName] - the middle items' name (default: '@mid').
 */
function updatePageProgressBar(options) {

    options = options || {};

    var doc = options.doc,
        createNewBars = false !== options.createNewBars,
        startName = options.startName || '@start',
        endName = options.endName || '@end',
        midName = options.midName || '@mid',
        items = doc.pageItems,
        start = items.item(startName),
        end = items.item(endName);

    if (
        !start.isValid || !start.paths || !start.paths.length
        || !end.isValid || !end.paths || !end.paths.length
    )
        return alert('Update Page Progress Failed:\nInvalid `' + startName + '` or `' + endName + '` items.');

    var startIndex = start.parentPage.documentOffset,
        endIndex = end.parentPage.documentOffset,
        startPoints = start.paths[0].pathPoints,
        endPoints = end.paths[0].pathPoints;

    if (startPoints.length !== endPoints.length)
        return alert('Update Page Progress Failed:\n`' + startName + '` or `' + endName + '` do not have same number of path points.');

    // update the `mid` on each page
    for (var i = startIndex + 1, mid, page, t; i < endIndex; i++) {

        page = doc.pages[i];
        mid = getThing(page.allPageItems, 'name', midName);

        if (
            mid && mid.isValid
            && mid.paths[0].pathPoints.length !== startPoints.length
        )
            // remove old faulty item
            mid.remove();

        if (!mid || !mid.isValid) {
            if (createNewBars)
                // create a new mid item
                mid = start.duplicate(page);
            else {
                // nothing to do on this page
                continue;
            }
        }

        // we can't change it if its layer is locked
        mid.itemLayer.locked = false;

        // name this so we can pick it up later
        mid.name = midName;

        // location between start and end, as a unit interval
        t = (i - startIndex) / (endIndex - startIndex);

        // interpolate each path point
        for (var j = 0; j < mid.paths[0].pathPoints.length; j++)
            interpolatePathPoint(start.paths[0].pathPoints[j], end.paths[0].pathPoints[j], mid.paths[0].pathPoints[j], t);

    }

};

/**
 * Transforms `mid` path point by performing
 * linear interpolation between `start` and `end`,
 * given interval `t`.
 * @author m1b
 * @version 2024-07-23
 * @param {PathPoint} start - the first PathPoint.
 * @param {PathPoint} end - the last PathPoint.
 * @param {PathPoint} mid - the middle PathPoint.
 * @param {Number} t - a unit interval, eg. 0.5 is half way between start and end.
 */
function interpolatePathPoint(start, end, mid, t) {

    mid.properties = {
        anchor: linearIntPoint(start.anchor, end.anchor),
        leftDirection: linearIntPoint(start.leftDirection, end.leftDirection),
        rightDirection: linearIntPoint(start.rightDirection, end.rightDirection),
    };

    function linearIntPoint(p0, p1) { return [linearInt(p0[0], p1[0]), linearInt(p0[1], p1[1])] };
    function linearInt(n, m) { return n + t * (m - n) };

};

/**
 * Returns a thing with matching property.
 * If `key` is undefined, evaluate the object itself.
 * @author m1b
 * @version 2024-04-21
 * @param {Array|Collection} things - the things to look through.
 * @param {String} [key] - the property name (default: undefined).
 * @param {*} value - the value to match.
 * @returns {*?} - the thing, if found.
 */
function getThing(things, key, value) {

    for (var i = 0, obj; i < things.length; i++)
        if ((undefined == key ? things[i] : things[i][key]) == value)
            return things[i];

};

 

 Edit 2024-07-23: fixed typo in script. Oops.

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