Skip to main content
Participant
July 2, 2025
Answered

unable to make script work CSV extraction

  • July 2, 2025
  • 1 reply
  • 256 views
So I have a jsx script, and I want jsx script to be able to extract data from the CSV. 
CSV file is the same one I use when I data merge - my script is supposed to sun after datamerge and attach links to pictures.
- problem is that it gives me an issue.

 

´

// ---- (1) Language selection dialog: Let user pick language code for links ----
function chooseLanguage() {
    var languageCodes = ["da", "en", "sv"];
    var languages     = ["Dansk", "English", "Swedish"];
    var w = new Window("dialog", "Vælg sprog til links");
    w.orientation = "column";
    w.alignChildren = "left";
    w.add("statictext", undefined, "Vælg sprog til hyperlinks:");
    var dropdown = w.add("dropdownlist", undefined, languages);
    dropdown.selection = 0;
    w.add("button", undefined, "OK");
    w.add("button", undefined, "Annuller");
    if (w.show() !== 1) return null; // Cancelled
    return languageCodes[dropdown.selection.index];
}

 

// ---- (2) Ask user for CSV used for Data Merge, explain consequences ----
function chooseCSVFile() {
    var csvFile = File.openDialog(
        "Vælg den samme CSV-fil med billeddata, som du brugte i Data Merge (*.csv).\n\nDet er vigtigt at vælge den IDENTISKE fil for at links bliver korrekte.",
        "*.csv",
        false
    );
    if (!csvFile || !csvFile.exists) {
        alert("Ingen fil valgt, eller filen findes ikke.\n\nHusk: Du SKAL vælge den samme CSV, som blev brugt til Data Merge.");
        return null;
    }
    csvFile.open("r");
    var fileContents = csvFile.read();
    csvFile.close();
    return fileContents;
}

 

// ---- (3) Parse CSV: returns mapping imageBasename --> URL ----
function parseCSVToLinkData(csvText) {
    var billedLinkData = {};
    var lines = csvText.split(/\r?\n/);
    if (lines.length < 2) {
        alert("CSV filen indeholder ikke nok data.");
        return null;
    }
    var header = lines[0].split(";");
    var idxImg = header.indexOf("@BILLEDE");
    var idxUrl = header.indexOf("HYPERLINK");
    if (idxImg === -1 || idxUrl === -1) {
        alert("CSV header mangler en eller begge af følgende kolonner: HYPERLINK, @BILLEDE");
        return null;
    }
    for (var l = 1; l < lines.length; l++) {
        var line = lines[l];
        if (!line.trim()) continue; // skip empty
        var cols = line.split(";");
        if (cols.length <= Math.max(idxImg, idxUrl)) continue; // skip malformed
        var imgPath = cols[idxImg];
        var url     = cols[idxUrl];
        if (!imgPath || !url) continue; // skip incomplete
        var imgName = File(imgPath).name.toLowerCase().trim(); // just the filename, eg 0013474.jpg
        billedLinkData[imgName] = url;
    }
    return billedLinkData;
}

 

// ---- (4) Scan all rectangles on all pages for image or name match ----
function scanRectangles(doc, linkData) {
    var totalRectangles = 0, matchedRectangles = 0;
    var matchList = [], notMatchedList = [], matchedItems = [];
    for (var p = 0; p < doc.pages.length; p++) {
        var page = doc.pages[p];
        for (var i = 0; i < page.allPageItems.length; i++) {
            var item = page.allPageItems[i];
            if (item.constructor.name === "Rectangle") {
                totalRectangles++;
                var key = null;
                if (item.images && item.images.length > 0 && item.images[0].itemLink != null) {
                    key = item.images[0].itemLink.name.toLowerCase().trim();
                } else if (item.name && item.name.length > 0) {
                    key = item.name.toLowerCase().trim();
                }
                if (key && linkData.hasOwnProperty(key)) {
                    matchedRectangles++;
                    matchList.push(key);
                    matchedItems.push({rect: item, key: key, url: linkData[key]});
                } else {
                    notMatchedList.push(item.name || "<uden navn>");
                }
            }
        }
    }
    return {
        total: totalRectangles,
        matched: matchedRectangles,
        matchList: matchList,
        notMatchedList: notMatchedList,
        matchedItems: matchedItems
    };
}

 

// ---- (5) Show long text in a scrollable, resizable ScriptUI dialog ----
function showScrollableDialog(title, content) {
    var win = new Window("dialog", title);
    win.orientation = "column";
    win.alignChildren = "fill";
    var editText = win.add("edittext", undefined, content, {multiline: true, scrolling: true});
    editText.minimumSize.height = 300;
    editText.minimumSize.width  = 600;
    var btnGroup = win.add("group");
    btnGroup.alignment = "right";
    btnGroup.add("button", undefined, "OK");
    win.show();
}

 

// ---- (6) Add overlays + hyperlinks for all matched rectangles ----
function addHyperlinks(doc, scanResults) {
    var attachedLinks = [];
    for (var i = 0; i < scanResults.matchedItems.length; i++) {
        var item = scanResults.matchedItems[i].rect;
        var key = scanResults.matchedItems[i].key;
        var url = scanResults.matchedItems[i].url;
        try {
            var overlay = item.parentPage.rectangles.add({
                geometricBounds: item.geometricBounds,
                fillColor: doc.swatches.itemByName("None"),
                strokeColor: doc.swatches.itemByName("None"),
                layer: item.itemLayer
            });
            overlay.sendToBack();
            overlay.transparencySettings.blendingSettings.opacity = 0;
            var dest;
            try {
                dest = doc.hyperlinkURLDestinations.itemByName(url);
                if (!dest.isValid) throw "invalid";
            } catch (_) {
                dest = doc.hyperlinkURLDestinations.add(url, {name: url});
            }
            var source = doc.hyperlinkPageItemSources.add(overlay, {hidden: false});
            doc.hyperlinks.add(source, dest);
            attachedLinks.push(key + " -> " + url);
        } catch (e) {
            $.writeln("Fejl: " + e.message);
        }
    }
    return attachedLinks;
}

 

// ==== Main routine ====

 

if (!app.documents.length) {
    alert("Åbn et InDesign-dokument først.");
    exit();
}

 

var langCode = chooseLanguage();
if (!langCode) exit();

 

var csvText = chooseCSVFile();
if (!csvText) exit();

 

var linkData = parseCSVToLinkData(csvText);
if (!linkData) exit();

 

var doc = app.activeDocument;
var scanResults = scanRectangles(doc, linkData);

 

var diagMsg =
    "Diagnostik før processeringen:\n\n" +
    "Der er " + scanResults.total + " rektangler i dokumentet.\n" +
    scanResults.matched + " af dem matcher rapporten og vil få overlay og hyperlink.\n" +
    (scanResults.matched > 0
        ? ("\nFørste 10 matchede:\n" + scanResults.matchList.slice(0, 10).join("\n")) : "") +
    (scanResults.notMatchedList.length > 0
        ? ("\n\nRektangler IKKE i rapporten (første 10):\n" +
            scanResults.notMatchedList.slice(0,10).join("\n"))
        : "");

 

showScrollableDialog("Diagnostik", diagMsg);

 

var attachedLinks = addHyperlinks(doc, scanResults);

 

if (attachedLinks.length > 0) {
    var msg =
        attachedLinks.length + " hyperlinks blev tilføjet til rektangler:\n\n" +
        attachedLinks.join("\n");
    showScrollableDialog("Hyperlinks tilføjet", msg);
} else {
    showScrollableDialog("Hyperlinks tilføjet", "Ingen hyperlinks blev tilføjet.");
Correct answer perfect_fighter1796

never mind, @m1b ! 

Seems like the following worked!

// --- Polyfill: String.prototype.trim (for old ExtendScript/CS versions) ---
if (!String.prototype.trim) {
    String.prototype.trim = function() {
        return this.replace(/^\s+|\s+$/g, "");
    };
}



1 reply

m1b
Community Expert
Community Expert
July 2, 2025

You are using a method of Array that ExtendScript doesn't natively have: indexOf

 

Put this polyfill into your code, before calling indexOf:

if (!Array.prototype.indexOf) Array.prototype.indexOf = (function (Object, max, min) {
    "use strict";

    return function indexOf(member, fromIndex) {

        if (
            this === null
            || this === undefined
        )
            throw TypeError("Array.prototype.indexOf called on null or undefined");

        var that = Object(this),
            Len = that.length >>> 0,
            i = min(fromIndex | 0, Len);

        if (i < 0) i = max(0, Len + i);
        else if (i >= Len) return -1;

        if (member === void 0) {
            for (; i !== Len; ++i)
                if (that[i] === void 0 && i in that)
                    return i; // undefined
        } else if (member !== member) {
            for (; i !== Len; ++i)
                if (that[i] !== that[i])
                    return i; // NaN
        } else for (; i !== Len; ++i)
            if (that[i] === member)
                return i; // all else

        return -1; // if the value was not found, then return -1

    };
})(Object, Math.max, Math.min);
Participant
July 3, 2025

thanks @m1b  - do you also have one for trim() ?

 

following error is what I retrieve right now:

line.trim is not a function 

perfect_fighter1796AuthorCorrect answer
Participant
July 3, 2025

never mind, @m1b ! 

Seems like the following worked!

// --- Polyfill: String.prototype.trim (for old ExtendScript/CS versions) ---
if (!String.prototype.trim) {
    String.prototype.trim = function() {
        return this.replace(/^\s+|\s+$/g, "");
    };
}