Search code examples
javascriptadobe-indesign

How to change this InDesign TOC hyperlinking script so that the entire paragraph, not just the page number, functions as a hyperlink?


I'm attempting to adapt a script (below) for creating a List of Tables in which each entry functions as a hyperlink within the pdf that, when clicked, takes you to the respective table's page. The script below currently works by identifying a character style — "TOC number" — which is applied only to that portion of each entry [=paragraph] which contains the actual number; the script reads the number, based on the character style, and turns the number into a hyperlink which points to that numbered page in the InDesign file.

(Note that the actual page, in InDesign, is a blank placeholder. The tables — of which there are hundreds — are created in Excel and inserted in Acrobat using "Replace pages..." as the last step of our production process. All of which is to say: Please save your suggestions if they involve using InDesign's automatic TOC features; we already use those extensively, but for the step of the process involving pdfs of Excel tables, it's just not an option — we want to adapt the script below, which already works and (almost) suits out needs.)

To illustrate, here's the part (circled in red) of each entry/paragraph in the List of Tables that the script currently makes into a hyperlink:

Here's what we want to do: We want to make the entire entry/paragraph that precedes the page number (circled again in red) into a clickable hyperlink that refers to corresponding page number.

I suppose the easiest way to do this would be by altering the script such that it locates the page number and then GREPs for the preceding paragraph break, and turns everything up to that paragraph break into part of the link? Alternatively, I've designed the paragraph style for List of Table entries such that each part — the table number, the title, the ellipses, and the page numbers — each has a distinct character style, delimited by tabs. So that might be another way of defining, in the script, what text preceding each page number should become part of the link.

Can anyone demonstrate a solution by editing this script? It already (almost) works!

/* Copyright 2016, Kasyan Servetsky
October 3, 2016
Written by Kasyan Servetsky
http://www.kasyan.ho.com.ua
e-mail: askoldich@yahoo.com */
//======================================================================================
var scriptName = "Make hyperlinks",
set, doc, swatchOK,
count = 0;

PreCheck();

//===================================== FUNCTIONS  ======================================
function Main() {
    app.findGrepPreferences = app.changeGrepPreferences = NothingEnum.NOTHING;
    app.findGrepPreferences.findWhat = "\\d+";
    app.findGrepPreferences.appliedCharacterStyle = "TOC number";
    var foundItems = doc.findGrep(true);
    app.findGrepPreferences = app.changeGrepPreferences = NothingEnum.NOTHING;

    if (foundItems.length == 0) ErrorExit("No page numbers were found in the current document.", true);

    for (var f = 0; f < foundItems.length; f++) {
        try {
            var sourceTextRef = foundItems[f].insertionPoints[0].paragraphs[0].texts[0];  

            if (sourceTextRef.fillColor != swatchOK) {
                MakeHyperlink(sourceTextRef);
            }
        }
        catch(err) {
            $.writeln(err.message + ", line: " + err.line);
        }
    }

    var report = count + " hyperlink" + ((count == 1) ? " was" : "s were") + " created.";
    alert("Finished. " + report, scriptName);
}
//--------------------------------------------------------------------------------------------------------------------------------------------------------
function MakeHyperlink(sourceTextRef) {
    var source, destination, hyperlink,
    pageNum = sourceTextRef.contents,
    obj = GetPage(pageNum);

    if (obj != null) {
        source = doc.hyperlinkTextSources.add(sourceTextRef);
        destination = obj.docDest.hyperlinkPageDestinations.add(obj.page);

        var name = "Page_" + pageNum;

        if (!doc.hyperlinks.itemByName(name).isValid) {
            hyperlink = doc.hyperlinks.add(source, destination, {name: name});
        }
        else {
            hyperlink = doc.hyperlinks.add(source, destination, {name: name + "_" + String(Math.random()).replace(/^0\./, "")});
        }

        if (hyperlink.isValid) {
            count++;
            sourceTextRef.fillColor = swatchOK;
        }
    }
}
//--------------------------------------------------------------------------------------------------------------------------------------------------------
function GetPage(pageNum) {
    var obj = null;

    for (var i = 0; i < app.documents.length; i++) {
        if (app.documents[i].pages.itemByName(pageNum).isValid) {
            obj = {page: app.documents[i].pages.itemByName(pageNum), docDest: app.documents[i]};
            break;
        }
    }

    return obj; 
}
//--------------------------------------------------------------------------------------------------------------------------------------------------------
function CheckSwatches() {
    if (doc.swatches.itemByName("===== OK =====") == null) {
        swatchOK = doc.colors.add({name : "===== OK =====", model : ColorModel.PROCESS, space : ColorSpace.RGB, colorValue : [0, 0, 0]});
    }
    else {
        swatchOK = doc.swatches.itemByName("===== OK =====");
    }
}
//--------------------------------------------------------------------------------------------------------------------------------------------------------
function PreCheck() {
    if (app.documents.length == 0) ErrorExit("Please open a document and try again.", true);
    doc = app.activeDocument;
    if (!app.activeDocument.saved) ErrorExit("The current document has not been saved since it was created. Please save the document and try again.", true);
    CheckSwatches();
    Main();
}
//--------------------------------------------------------------------------------------------------------------------------------------------------------
function ErrorExit(error, icon) {
    alert(error, scriptName, icon);
    exit();
}
//--------------------------------------------------------------------------------------------------------------------------------------------------------

Solution

  • Below is the solution, which I worked out myself. My problem was that I was trying to do too much with one variable (sourceTextRef). Once I defined a second variable (tabPnum) that holds only the page number — as distinct from sourceTextRef, which I redefined to hold the entire paragraph in which each page number is found — and I passed that new parameter to the MakeHyperlink function, the script worked:

    //======================================================================================
    var scriptName = "TOC links",
    set, doc, swatchOK,
    count = 0;
    
    PreCheck();
    
    //===================================== FUNCTIONS  ======================================
    function Main() {
        app.findGrepPreferences = app.changeGrepPreferences = NothingEnum.NOTHING;
        app.findGrepPreferences.findWhat = "\\d+";      
        app.findGrepPreferences.appliedParagraphStyle = app.activeDocument.paragraphStyleGroups.item("publications").paragraphStyleGroups.item("TOC & Acknowledgements").paragraphStyles.item("TOC Table/Figure entry");
        var foundItems = doc.findGrep(true);
        app.findGrepPreferences = app.changeGrepPreferences = NothingEnum.NOTHING;
    
        if (foundItems.length == 0) ErrorExit("No page numbers were found in the current document.", true);
    
        for (var f = 0; f < foundItems.length; f++) {
            try {
                var tabPnum = foundItems[f];      // foundItems[f] returns the page number . 
    
                var sourceTextRef = foundItems[f].words[0].lines[0].paragraphs[0];   // returns the full paragraph.
    
    $.writeln(sourceTextRef.contents);
    
                if (sourceTextRef.fillColor != swatchOK) {
                    MakeHyperlink(sourceTextRef, tabPnum);  // added tabPnum here (necessary for passing parameter to function below).
                }
            }
            catch(err) {
                $.writeln(err.message + ", line: " + err.line);
            }
        }
    
        var report = count + " hyperlink" + ((count == 1) ? " was" : "s were") + " created.";
        alert("Finished. " + report, scriptName);
    }
    //--------------------------------------------------------------------------------------------------------------------------------------------------------
    function MakeHyperlink(sourceTextRef, tabPnum) { //added parameter tabPnum as argument here)
        var source, destination, hyperlink, 
        pageNum = tabPnum.contents,   //  changed this line so that pageNum is defined by tabPnum. 
    
        obj = GetPage(pageNum);
    
        if (obj != null) {
            source = doc.hyperlinkTextSources.add(sourceTextRef);
            destination = obj.docDest.hyperlinkPageDestinations.add(obj.page);
    
            var name = "Page_" + pageNum;
    
            if (!doc.hyperlinks.itemByName(name).isValid) {
                hyperlink = doc.hyperlinks.add(source, destination, {name: name});
            }
            else {
                hyperlink = doc.hyperlinks.add(source, destination, {name: name + "_" + String(Math.random()).replace(/^0\./, "")});
            }
    
            if (hyperlink.isValid) {
                count++;
                sourceTextRef.fillColor = swatchOK;
            }
        }
    }
    //--------------------------------------------------------------------------------------------------------------------------------------------------------
    function GetPage(pageNum) {
        var obj = null;
    
        for (var i = 0; i < app.documents.length; i++) {
            if (app.documents[i].pages.itemByName(pageNum).isValid) {
                obj = {page: app.documents[i].pages.itemByName(pageNum), docDest: app.documents[i]};
                break;
            }
        }
    
        return obj; 
    }
    //--------------------------------------------------------------------------------------------------------------------------------------------------------
    function CheckSwatches() {
        if (doc.swatches.itemByName("===== OK =====") == null) {
            swatchOK = doc.colors.add({name : "===== OK =====", model : ColorModel.PROCESS, space : ColorSpace.CMYK, colorValue : [0, 0, 0, 100]});
    
        }
        else {
            swatchOK = doc.swatches.itemByName("===== OK =====");
        }
    }
    //--------------------------------------------------------------------------------------------------------------------------------------------------------
    function PreCheck() {
        if (app.documents.length == 0) ErrorExit("Please open a document and try again.", true);
        doc = app.activeDocument;
        if (!app.activeDocument.saved) ErrorExit("The current document has not been saved since it was created. Please save the document and try again.", true);
        CheckSwatches();
        Main();
    }
    //--------------------------------------------------------------------------------------------------------------------------------------------------------
    function ErrorExit(error, icon) {
        alert(error, scriptName, icon);
        exit();
    }
    //--------------------------------------------------------------------------------------------------------------------------------------------------------