Copy link to clipboard
Copied
var doc = app.activeDocument;
var counter = 1;
for (var i = 0; i < doc.pages.length; i++) {
var page = doc.pages[i];
// Create a text frame on the page
var tf = page.textFrames.add({
geometricBounds: [20, 20, 100, 100] // Adjust as needed
});
// Find all footnotes on the page by iterating through all stories in the document
var stories = doc.stories;
alert("Found " + stories.length + " stories.");
var s = stories[0];
// Check if any text frame of this story is on the current page
var frames = s.textContainers;
var isOnPage = false;
for (var t = 0; t < frames.length; t++) {
if (frames[t].parentPage && frames[t].parentPage == page) {
isOnPage = true;
break;
}
}
if (!isOnPage) continue;
var fns = s.footnotes;
alert("Found " + fns.length + " footnotes in story " + (s + 1) + ".");
// Iterate through all footnotes in the story
for (var f = 0; f < fns.length; f++) {
// Find paragraphs in footnote that begin with "ØØ§Ø´ÙŠØ©"
var paras = fns[f].paragraphs;
// alert("Found " + paras.length + " paragraphs in footnote " + (f + 1) + ".");
for (var p = 0; p < paras.length; p++) {
// alert(paras[p].contents);
// this line is not working. there are pragraphs that start with prefix text, but the if statement doesn't catch them (need help here)
if (paras[p].contents.indexOf(":ØØ§Ø´ÙŠØ©") === 0) {
// Cut the paragraph text
var paraText = paras[p].contents;
alert("Found footnote: " + paraText);
paras[p].remove();
// Replace footnote marker in main text with counter
var marker = fns[f].storyOffset;
marker.contents = counter.toString() + " ";
// delete the footnote marker
// works but in next iteration length of fns changes and error occurs
// fns[f].remove();
// Insert counter and text in created text frame
tf.contents += counter + "- " + paraText;
counter++;
}
}
}
}
alert("Finished processing footnotes.");this is what i want to achieve from above code:
Hi @Moiz5FB2 I had a look at your script so far and I have re-written it the way I would like it. This can be just for your learning, it doesn't mean you must do it this way—there are many correct ways—but just that I prefer this approach.
Here are some notes to help:
1. The script starts by collecting all the footnotes matching the target text at the start using findGrep and stores the contents and a reference to the footnote marker in the main story.
It stores a {contents: 'my footnote s
...Copy link to clipboard
Copied
Try using Find&Change to find your text - then process the results - it will save you time of checking every Story and every Paragraph.
Maybe the search phrase you've entered - is "beyond" JS Unicode capabilities?
Copy link to clipboard
Copied
i guessed that too and tried with english footnote text and it did not work.
Copy link to clipboard
Copied
i guessed that too and tried with english footnote text and it did not work.
By @Moiz5FB2
What exactly have you tried?
Copy link to clipboard
Copied
My idea is to use InDesign's F&C - and either type text in the "Find What:" field and then run script (*) that will work with findText() or findGREP() - or save the Query:
and then load it in the script - at the beginning.
(*) could work like this:
Copy link to clipboard
Copied
Try replace this
if (paras[p].contents.indexOf(":ØØ§Ø´ÙŠØ©") === 0) {
With:
var content = paras[p].contents.replace(/[\u200E\u200F\u202A-\u202E\uFEFF]/g, "").trim(); // Remove hidden LTR/RTL markers
if (content.indexOf(":ØØ§Ø´ÙŠØ©") === 0) {
Should remove invisible characters (Unicode directionality marks), and trims whitespace that could break your match.
Try to iterate backwards
replace
for (var f = 0; f < fns.length; f++) {
with
for (var f = fns.length - 1; f >= 0; f--) {
Copy link to clipboard
Copied
thanks for prompt reply.
tried the changes and got this error:
JavaScript Error!
Error Number: 24
Error String: paras[p].contents.replace().trim is not a function
Source: var content = paras[p].contents.replace(/[\u200E\u200F\u202A-\u202E\uFEFF]/g, "").trim();
Copy link to clipboard
Copied
Ah right sorry about that
var content = String(paras[p].contents).replace(/[\u200E\u200F\u202A-\u202E\uFEFF]/g, "").trim();
If you have a sample file I can troubleshoot the script to see if I can get it working for you
Copy link to clipboard
Copied
Copy link to clipboard
Copied
@Moiz5FB2 - I haven't tested the attached file yet, but for the trim functionality to work within the script, you'll need to include its prototype definition:
if (!String.prototype.trim) {
(function () {
// Ensures trimming of BOM and non-breaking spaces (NBSP)
var rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;
String.prototype.trim = function () {
return this.replace(rtrim, '');
};
})();
}
Copy link to clipboard
Copied
thanks for the suggested code for trim, now no more errors with trim.
but the below code still does not work:
for (var f = fns.length - 1; f >= 0; f--) {
// Find paragraphs in footnote that begin with "ØØ§Ø´ÙŠØ©"
var paras = fns[f].paragraphs;
// alert("Found " + paras.length + " paragraphs in footnote " + (f + 1) + ".");
for (var p = 0; p < paras.length; p++) {
// Remove hidden LTR/RTL markers
var content = String(paras[p].contents).replace(/[\u200E\u200F\u202A-\u202E\uFEFF]/g, "").trim();
alert(content);
if (content.indexOf("a") === 0) {
// Cut the paragraph text
var paraText = paras[p].contents;
alert("Found footnote: " + paraText);
paras[p].remove();
// Replace footnote marker in main text with counter
var marker = fns[f].storyOffset;
marker.contents = counter.toString() + " ";
// delete the footnote marker
// works but in next iteration length of fns will change and error will occur
// fns[f].remove();
// Insert counter and text in created text frame
tf.contents += counter + "- " + paraText;
counter++;
}
}
}the "alert(content);" shows all footnote contents correctly, but in next line indexOf fails to detect anything.
Copy link to clipboard
Copied
I'm not having much luck either
On the surface it should be working but I'm missing something - I build in a few checks to see what's going on, and can get a sample return, but I am lost
@m1b @Charu Rajput any ideas what's going wrong?
Fairly new to scripting - hope it's something simple.
if (!String.prototype.trim) {
String.prototype.trim = function () {
return this.replace(/^\s+|\s+$/g, '');
};
}
var doc = app.activeDocument;
var counter = 1;
for (var i = 0; i < doc.pages.length; i++) {
var page = doc.pages[i];
var tf = page.textFrames.add({
geometricBounds: [20, 20, 100, 100]
});
var stories = doc.stories;
for (var s = 0; s < stories.length; s++) {
var story = stories[s];
var frames = story.textContainers;
var isOnPage = false;
for (var t = 0; t < frames.length; t++) {
if (frames[t].parentPage && frames[t].parentPage === page) {
isOnPage = true;
break;
}
}
if (!isOnPage) continue;
var fns = story.footnotes;
for (var f = fns.length - 1; f >= 0; f--) {
var paras = fns[f].paragraphs;
for (var p = 0; p < paras.length; p++) {
var content = String(paras[p].contents)
.replace(/[\u200E\u200F\u202A-\u202E\uFEFF]/g, "")
.trim();
alert("Checking paragraph: " + content);
var pattern = /^[:\u0600-\u06FF]/;
if (pattern.test(content)) {
alert("Matched paragraph: " + content);
var paraText = paras[p].contents;
paras[p].remove();
var marker = fns[f].storyOffset;
marker.contents = counter.toString() + " ";
tf.contents += counter + "- " + paraText + "\r";
counter++;
fns[f].remove();
break;
}
}
}
}
}
alert("Finished processing footnotes.");
Copy link to clipboard
Copied
I asked ChatGPT about this and it suggested this:
Copy link to clipboard
Copied
var doc = app.activeDocument;
var counter = 1;
if (!String.prototype.trim) {
(function () {
// Ensures trimming of BOM and non-breaking spaces (NBSP)
var rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;
String.prototype.trim = function () {
return this.replace(rtrim, '');
};
})();
}
for (var i = 0; i < doc.pages.length; i++) {
var page = doc.pages[i];
// Create a text frame on the page
var tf = page.textFrames.add({
geometricBounds: [20, 20, 100, 100] // Adjust as needed
});
// Find all footnotes on the page by iterating through all stories in the document
var stories = doc.stories;
// alert("Found " + stories.length + " stories.");
var s = stories[0];
// Check if any text frame of this story is on the current page
var frames = s.textContainers;
var isOnPage = false;
for (var t = 0; t < frames.length; t++) {
if (frames[t].parentPage && frames[t].parentPage == page) {
isOnPage = true;
break;
}
}
if (!isOnPage) continue;
var fns = s.footnotes;
// Iterate through all footnotes in the story
for (var f = fns.length - 1; f >= 0; f--) {
var paras = fns[f].paragraphs;
// for (var p = 0; p < paras.length; p++) {
for (var p = paras.length-1; p >=0; p--) {
var content = paras[p].contents;
alert(content);
if (endsWithArabicWord(content, "(ØØ§Ø´ÙŠØ©)")) {
// Cut the paragraph text
var paraText = paras[p].contents;
// alert("Found footnote: " + paraText);
paras[p].remove();
// Replace footnote marker in main text with counter
var marker = fns[f].storyOffset;
marker.contents = counter.toString() + " ";
// delete the footnote marker
fns[f].remove();
// Insert counter and text in created text frame
tf.contents += counter + "- " + paraText;
counter++;
}
}
}
}
alert("Finished processing footnotes.");
function endsWithArabicWord(text, word) {
if (text == null || word == null) return false;
// Fallback trim (remove leading/trailing spaces)
text = text.replace(/^\s+|\s+$/g, '');
// Escape special regex characters in the word
var safeWord = word.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
// Check for optional space or punctuation before the word
var pattern = new RegExp("[\\s\\u060C\\u061B\\u061F\\u0640\\u200C\\u200F\\u202B\\u200E\\u202C\\u202D\\u202E]*" + safeWord + "$");
return pattern.test(text);
}I changed the strategy, now i process all paragraphs that end with particular word.
the above code works as expected.
now the issue is that the loops run backwards to avoid length errors.
because of that the marker numbers in body text are inserted in reverse order.
please provide your expert help and suggestions on this.
Copy link to clipboard
Copied
Hi @Moiz5FB2 I had a look at your script so far and I have re-written it the way I would like it. This can be just for your learning, it doesn't mean you must do it this way—there are many correct ways—but just that I prefer this approach.
Here are some notes to help:
1. The script starts by collecting all the footnotes matching the target text at the start using findGrep and stores the contents and a reference to the footnote marker in the main story.
It stores a {contents: 'my footnote string here', marker: FOOTNOTE_SYMBOL} object in an array, organised by in the footnoteDetailsByPage object accessed by the page's document offset. This is the most complex part I think if you haven't stored properties in plain Objects before. For example we can access it like this:
var secondFootnoteMarkerOnPageOne = footnoteDetailsByPage[0][1].marker;
var sixthFootnoteContentsOnPageThree = footnoteDetailsByPage[2][5].contents;
var countOfFootnotesProcessedOnPageFive = footnoteDetailsByPage[4].length;
This is what it looks like in the debugger, after running your sample document with my boxes added:
2. After collecting the info in footnoteDetailsByPage, we start a new loop, going over every page stored in footnoteDetailsByPage and then looping over every element of the array stored there. We go forwards so the indexing will be easier.
3. I've wrapped whole script in the app.doScript call.
app.doScript(main, ScriptLanguage.JAVASCRIPT, undefined, UndoModes.ENTIRE_SCRIPT, 'Process Footnotes');
This is very important for a script like this, because it means that you can undo the whole script's work in one go.
4. To see more of what is happening, I collect "results" and show them at the end.
I hope that can be useful to you.
- Mark
function main() {
var doc = app.activeDocument;
// set up the find grep
app.findGrepPreferences = NothingEnum.NOTHING;
app.findChangeGrepOptions.includeFootnotes = true;
app.findChangeGrepOptions.includeHiddenLayers = false;
app.findChangeGrepOptions.includeLockedLayersForFind = false;
// find footnote texts starting with "footnote:"
app.findGrepPreferences.findWhat = '^~F\\s*ØØ§Ø´ÙŠØ©:';
var footnoteTexts = doc.findGrep();
if (!footnoteTexts)
return alert('Could not find any footnotes.');
// we will store all the contents here, organised by page index
var footnoteDetailsByPage = {};
// collect details for each matched footnote, then remove it
for (var i = footnoteTexts.length - 1; i >= 0; i--) {
var footnote = footnoteTexts[i].parent,
page = footnote.characters[0].parentTextFrames[0].parentPage;
if (!footnoteDetailsByPage[page.documentOffset])
// initialize with a new array
footnoteDetailsByPage[page.documentOffset] = [];
// add details about this footnote
footnoteDetailsByPage[page.documentOffset].unshift({
// remove hidden LTR/RTL markers and trim whitespace
contents: footnote.contents.replace(/^\s+|\s+$|[\u200E\u200F\u202A-\u202E\uFEFF]/g, ""),
// we need this for later
marker: footnote.storyOffset.parentStory.characters[footnote.storyOffset.index],
});
// remove the footnote
footnoteTexts[i].remove();
}
var results = [];
// process each page
for (var pageOffset in footnoteDetailsByPage) {
if (!footnoteDetailsByPage.hasOwnProperty(pageOffset))
continue;
var page = doc.pages[pageOffset];
var footnoteDetailsForThisPage = footnoteDetailsByPage[pageOffset];
var contents = [];
for (var i = 0; i < footnoteDetailsForThisPage.length; i++) {
var footnoteDetail = footnoteDetailsForThisPage[i];
// replace the footnote marker with the current index
footnoteDetail.marker.contents = String(i + 1);
// add the contents to this page's contents list
contents.push((i + 1) + '- ' + footnoteDetail.contents);
}
// create a text frame on the page, with contents from each processed footnote
var tf = page.textFrames.add({
geometricBounds: [20, 20, 100, 100], // Adjust as needed
contents: contents.join('\r'),
});
results.push('Page ' + page.name + ': matched ' + contents.length + ' footnotes.')
}
alert("Finished processing footnotes.\n" + results.join('\n'));
};
app.doScript(main, ScriptLanguage.JAVASCRIPT, undefined, UndoModes.ENTIRE_SCRIPT, 'Process Footnotes');
Edit: added image for clarity.
Copy link to clipboard
Copied
Thank you very much for the perfectly functioning code and the clear, detailed explanation of your approach and the steps you followed to achieve the desired outcome.
I truly appreciate the time and effort you saved me.
Thanks again, with best regards.
Copy link to clipboard
Copied
I'm glad to hear it was helpful. All the best with your scripting!
Copy link to clipboard
Copied
You're working with contents of the found results - instead of the Text object.
If you use InDesign's functionality to search for text - text or GREP - you'll avoid all your problems.
You'll also preserve formatting.
Looks like Mark already addressed most of it.
Get ready! An upgraded Adobe Community experience is coming in January.
Learn more