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/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
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
...
@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
...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;
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')
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]];
}
}
Copy link to clipboard
Copied
@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.