Copy link to clipboard
Copied
Hi Community.
I'm trying to write a small script that would fix the inconsistent numbered list paragraphs when the date of creation of text frames does not he follow the layout.
So here's the script:
var myDocument = app.documents.item(0);
var myParaStyle = "numbered";
var myFrames = [];
//create collection
for (i = 0; i < myDocument.textFrames.length; i++) {
if (myDocument.textFrames.paragraphs[0].appliedParagraphStyle.name == myParaStyle) {
myFrames.push(myDocument.textFrames);
}
}
//sort collection
function sortFramesinArray(a, b) {
if (Math.round(a.geometricBounds[1]) === Math.round(b.geometricBounds[1])) {
return Math.round(a.geometricBounds[0]) - Math.round(b.geometricBounds[0]);
}
return Math.round(a.geometricBounds[1]) - Math.round(b.geometricBounds[1]);
}
myFrames.sort(sortFramesinArray);
// cut and paste in place
for (i = 0; i < myFrames.length; i++) {
myFrames.select();
app.cut();
app.pasteInPlace();
}
I've tested it on a very basic CS6 document and it seems to be working.
Now, tested by another user on a CS5.5 document, the script returns a 45 "Object is Invalid" error.
See here: automatic numbering gone wrong
Could anyone advice on why this happened? Is a script issue (I'm a beginner), a document issue, or a Indd version issue?
Thanks in advance for your enlightening help.
Vinny
Hi vinny38
Here you are:
...//Main routine
var main = function() {
//======VAR======//
var doc = app.properties.activeDocument,
fgp = app.findGrepPreferences.properties,
ps, found, n, nText, data = {}, page, ptf, vb;
//Exit if no documents open
if ( !doc ) return;
ps = doc.paragraphStyles.itemByName ("listeNum");
//Exit if paragraph style is not present
if ( !ps.isValid ) {
alert("The style \"listeNum\" is required !\rScript exits" );
return;
}
//Setting grep prefs for findin
Copy link to clipboard
Copied
You are experiencing a reference error value.
Simple question :
You write
var myDocument = app.documents.item(0);
Then
myDocument.textFrames.length;
What if the end user runs your your script but no documents is actually open ?
Answer: he will get the error you are describing.
ALWAYS check twice before using objects properties:
if ( app.documents.length > 0 ) {
var doc = app.documents[0]; //or app.activeDocument;
//Next
}
else {
alert("Please open a document first then run teh script…" );
}
Copy link to clipboard
Copied
Hi Loic
Thanks for your answer.
Definitely right about checking if there's a document open. I'll add that ^^
About the issue I have, we have realized that grouped frames were the trouble-makers.
It does make sense, but I couldn't find a way to get textFrames while grouped.
As a workaround, I just add a "ungroup everything" function, but it's quite dirty and unsatisfactory.
Any better idea would be welcome ^^
Copy link to clipboard
Copied
There are several ways to approach your problem and none is better than the other. However how are you list numbers generated. Is it a numbering option of the paragraph Style or some typed in text ?
Copy link to clipboard
Copied
Yes, numbered list which is set up in paragraph style...
Copy link to clipboard
Copied
Something like that maybe ?
//Main routine
var main = function() {
//======VAR======//
var doc = app.properties.activeDocument,
fgp = app.findGrepPreferences.properties,
ps, found, n, nText, data = {}, page, ptf;
//Exit if no documents open
if ( !doc ) return;
ps = doc.paragraphStyles.itemByName ("listeNum");
//Exit if paragraph style is not present
if ( !ps.isValid ) {
alert("The style \"listeNum\" is required !\rScript exits" );
return;
}
//Setting grep prefs for finding texts
app.findGrepPreferences = null;
app.findGrepPreferences.properties = {
findWhat:".+",
appliedParagraphStyle:ps,
}
found = doc.findGrep();
n = found.length;
//Looping through text results
//and store text frames reference inside object
//for further processing
while ( n-- ) {
nText = found
; ptf = nText.parentTextFrames;
if ( !ptf.length ) continue;
ptf = ptf[0];
if ( !ptf.parentPage ) continue;
data[ptf.parentPage.id] = data[ptf.parentPage.id] || {};
data[ptf.parentPage.id][ptf.id] = {
tf:ptf,
num:Number ( nText.paragraphs[0].bulletsAndNumberingResultText.replace (/[^\d]+/g, '') ),
y : ptf.visibleBounds[0]
};
}
//Reordering frames per page
for ( prop in data ) {
reorderTFS ( data[prop] );
}
app.findGrepPreferences.properties = fgp;
}
//reordering routine
function reorderTFS ( tfsObj ) {
var posArr = [], obj, yArr = [], n = 0, tf;
//generating two arrays to be sorted
//One per "index"
//One per y location
for ( prop in tfsObj ) {
obj = tfsObj[prop];
posArr.push ( obj );
yArr.push ( obj );
}
posArr = posArr.sort( function(a,b){
return a.num>b.num
} );
yArr = yArr.sort ( function(a,b){
return a.y>b.y
} );
//Then looping through one of the two arrays
//And relocate given the expected natural location
n = posArr.length;
while ( n-- ) {
tf = posArr
.tf; tf.move ( [tf.visibleBounds[1], yArr
.y ] ); }
}
var u;
//Running routine
app.doScript ( "main()",u,u,UndoModes.ENTIRE_SCRIPT, "The Script" );
Should work whatever the frames are grouped or not. Oh a possible flaw you may want to adjust is that locked layers or objects will make the script fail.
Copy link to clipboard
Copied
Salut Loïc
Looks promising... definitely a different approach. In fact you just rewrite everything... I didn't ask for that much really ^^
I love the fact that you don't have to use the select() method, which make things much faster!
However, results can be very inconsistent.
See example below (with nothing grouped):
I tried to understand your script, but it's way to expert for me and I'm stuck understanding where things go wrong...
Copy link to clipboard
Copied
Would you mind sending me your demo files. I know I could redo it but I would be certain to reproduce the behaviour you describe. It's seems it's mostly a "x" location issue.
Regarding to code, UI operations are definitively to be avoided (select, copy, paste, pasteInPlace).
Copy link to clipboard
Copied
Hi vinny38
Here you are:
//Main routine
var main = function() {
//======VAR======//
var doc = app.properties.activeDocument,
fgp = app.findGrepPreferences.properties,
ps, found, n, nText, data = {}, page, ptf, vb;
//Exit if no documents open
if ( !doc ) return;
ps = doc.paragraphStyles.itemByName ("listeNum");
//Exit if paragraph style is not present
if ( !ps.isValid ) {
alert("The style \"listeNum\" is required !\rScript exits" );
return;
}
//Setting grep prefs for finding texts
app.findGrepPreferences = null;
app.findGrepPreferences.properties = {
findWhat:".+",
appliedParagraphStyle:ps,
}
found = doc.findGrep();
n = found.length;
//Looping through text results
//and store text frames reference inside object
//for further processing
while ( n-- ) {
nText = found
; ptf = nText.parentTextFrames;
if ( !ptf.length ) continue;
ptf = ptf[0];
if ( !ptf.parentPage ) continue;
vb = ptf.visibleBounds;
data[ptf.parentPage.id] = data[ptf.parentPage.id] || {};
data[ptf.parentPage.id][ptf.id] = {
tf:ptf,
num:Number ( nText.paragraphs[0].bulletsAndNumberingResultText.replace (/[^\d]+/g, '') ),
x : vb[1],
y : vb[0]
};
}
//Reordering frames per page
for ( prop in data ) {
reorderTFS ( data[prop] );
}
app.findGrepPreferences.properties = fgp;
}
//reordering routine
function reorderTFS ( tfsObj ) {
var posArr = [], obj, yArr = [], n = 0, tf;
//generating two arrays to be sorted
//One per "index"
//One per y location
for ( prop in tfsObj ) {
obj = tfsObj[prop];
posArr.push ( obj );
yArr.push ( obj );
}
//Sorting function to get frames ordered by "bullet" num
posArr = posArr.sort( function(a,b){
return a.num>b.num
} );
//Sorting function to get frames ordered by x,y locations
yArr = yArr.sort ( function(a,b){
return (a.y==b.y)? a.x > b.x : a.y > b.y ;
} );
//Then looping through one of the two arrays
//And relocate given the expected natural location
n = posArr.length;
while ( n-- ) {
tf = posArr
.tf;
tf.move ( [yArr
.x, yArr .y ] ); }
}
var u;
//Running routine
app.doScript ( "main()",u,u,UndoModes.ENTIRE_SCRIPT, "The Script" );
HTH
Loic
Copy link to clipboard
Copied
Amazing!
Works like a charm... You definitely are a great scripter! This one goes straight to my scripts favorites ^^
Now, I'll try to get into this code and try to understand it, I'm sure I'll learn a lot.
Right now, I'm trying to understand how to rewrite the yArr.sort function in order to sort from top to bottom... It's a struggle
Copy link to clipboard
Copied
A possible hiccup is that it wouldn't work with letter based numbering (as in i. … ). The script would then need to convert those indeces to their corresponding numeral values but as you are in a "controlled" environment…
[EDIT] Another hiccup too is that i didn't deal with layers locked state. Another reason for the script to fail[/EDIT]
Now, I'll try to get into this code and try to understand it, I'm sure I'll learn a lot.
The logic isn't that hard.
1) grab text frames with GREP
2) retrieve every single found text frame properties
3) Setting two sorted arrays, one for ordering text frames per list index value, other for ordering text frames per their x,y location
4) Once done looping through index values array and apply the x/y props of the x,y array for the corresponding array index
and move item accordingly.
Another option would have been to move stories themselves but I am fine with this approach.
Glad it helped.
Loic
Copy link to clipboard
Copied
Yeah, not soooo hard to understand (but still, a little bit for me ^^)
As I said, I tried to change script in order to be able to sort it from top to bottom instead.
For some reason, it failed all the time.
I finally figured it out (I think).
There seems to be some rounding issue. Sometimes visibleBounds (as well as geometricBounds) have a slight value difference even if the frames are aligned. Don't know why really, but using Math.Round() function seems to be fixing it.
What do you think about it?
See example below:
About hiccups, I think I can live with them. Besides, adding a "locked layer" alert should not be so difficult.
Although I would add another "hiccup": Formatting obviously follows the frame it is applied to.
Anyway... it's a great script!
Merci encore !
Vinny
Find more inspiration, events, and resources on the new Adobe Community
Explore Now