Skip to main content
suep89793525
Known Participant
June 22, 2023
Question

Batch converting layers on insets...So close, but need help!

  • June 22, 2023
  • 1 reply
  • 1446 views

Hi--I found a free Javascript online that ALMOST does what I want it to.  But it appears to be missing (for me) the most important feature.  I'm hoping that someone here can modify it for me or point me towards something that will accomplish the task.  (The original author seems to have been offline since 2019.)

 

What I want to create is a document made up of several placed external files.  Each placed file is multi-lingual, with translations contained on separate layers. For example, each placed file has English text on layer EN, French text on layer FR, Spanish text on layer ES, etc.  My goal is to run the script while in the "parent" document--allowing me to choose the desired translated layer and have all of the placed files "flip" to the correct translation.  The script I found (text file version attached) ALMOST works...however, it does not seem to "batch" convert all the placed files at once. For example, I placed 3 files on the parent document as a test, ran the script, and selected FR. It only "flipped" one of them.  The others stayed as I originally saved them, with all four layers showing.  I only got the other two to "flip" by selecting each one individually and running the script on each file.

 

What's missing from the attached script that won't allow me to run this and "flip" everything at once?  Can it be fixed easily?  Or is there a better solution?  I appreciate any help that I can get with this.  Thank you!  

 
This topic has been closed for replies.

1 reply

m1b
Community Expert
Community Expert
June 23, 2023

Hi @suep89793525, the script you linked by CrazyPanda is great, and I probably could have adjusted it to run the way you want, but it wasn't structured ideally for that, and anyway I always like to learn, so I have written my own version. Please give it a try. Hasn't been tested much, so please let me know here if you find a bug and I will fix. It should work with placed .ai, .pdf, and .indd files.

- Mark

 

 

/**
 * Sets layer visibility of all placed items in active document.
 * All layers are set to invisible, unless the layer name exists
 * in `settings.visibleLayerNames`
 * NOTES:
 * - Adjust the settings object if you want to pre-populate
 *   the layerNames and visibleLayer.
 * - Do not specify `layerNames` in settings if you want
 *   to choose from *every* layer in every placed item.
 * @author m1b
 * @discussion https://community.adobe.com/t5/indesign-discussions/batch-converting-layers-on-insets-so-close-but-need-help/m-p/13885184
 */

var settings = {
    // layerNames: ['EN', 'ES', 'FR'], // uncomment this to limit the choices
    visibleLayerNames: ['FR'],
    showUI: true,
};

function main() {

    var doc = app.activeDocument,
        items = getLinkedPdfs(doc),
        identifiers = [];

    if (items.length == 0) {
        alert("No suitable linked files in document.");
        return;
    }

    // keep a stringified identifier
    // to bypass a bug with indesign
    // that assigns a new id every
    // time layer visibility is updated
    for (var i = 0; i < items.length; i++)
        identifiers.push(stringify(items[i]));

    // if no layer names specified
    // in settings, collect them all
    settings.layerNames = settings.layerNames || getLayerNames(items);
    settings.visibleLayerNames = settings.visibleLayerNames || [];

    // show UI
    if (
        settings.showUI
        && chooseLayers(settings) == false
    )
        // user cancelled UI
        return;

    // set each layer's visibility
    for (var i = items.length - 1; i >= 0; i--) {

        if (!items[i].isValid)
            continue;

        var len = items[i].graphicLayerOptions.graphicLayers.length;
        items[i].graphicLayerOptions.updateLinkOption = UpdateLinkOptions.KEEP_OVERRIDES;

        for (var j = 0; j < len; j++) {

            // get the item each time (because of bug mentioned earlier)
            var item = itemByStringifier(getLinkedPdfs(doc), identifiers[i], stringify);

            if (item.isValid)
                item.graphicLayerOptions.graphicLayers[j].currentVisibility = indexOf(item.graphicLayerOptions.graphicLayers[j].name, settings.visibleLayerNames) !== -1;

        }
    }

};

app.doScript(main, ScriptLanguage.JAVASCRIPT, undefined, UndoModes.ENTIRE_SCRIPT, 'Set Layer Visibility');


/**
 * Returns array of linked pdf files.
 * @author m1b
 * @version 2023-06-24
 * @param {Document} doc - an Indesign Document.
 * @returns {Array<PageItem>}
 */
function getLinkedPdfs(doc) {

    var items = doc.links.everyItem().parent,
        linkedPdfs = [];

    for (var i = 0; i < items.length; i++)
        if (
            items[i].hasOwnProperty('pdfAttributes')
            && items[i].hasOwnProperty('graphicLayerOptions')
            && items[i].itemLink.status === LinkStatus.NORMAL
        )
            linkedPdfs.push(items[i]);

    return linkedPdfs;

};


/**
 * Gets a single item from array `items` based
 * on the result of a stringifier function.
 * @author m1b
 * @version 2023-06-23
 * @param {Array<PageItem>} items - an array of items from which to choose.
 * @param {String} identifier - the string to search for.
 * @param {Function} stringifier - a stringifying function.
 * @returns {PageItem}
 */
function itemByStringifier(items, identifier, stringifier) {

    for (var i = 0; i < items.length; i++)
        if (stringifier(items[i]) === identifier)
            return items[i];

};


/**
 * Generate a string just for the purpose
 * of identifying a particular linked item.
 * @author m1b
 * @version 2023-06-23
 * @param {PageItem} item - an Indesign placed item.
 * @returns {String}
 */
function stringify(item) {

    var str = '',
        delim = '|'

    if (
        item.itemLink
        && item.itemLink.filePath
    )
        str += item.itemLink.filePath + delim;

    if (item.graphicLayerOptions.graphicLayers.length > 0)
        str += item.graphicLayerOptions.graphicLayers.everyItem().name.join(delim) + delim;

    if (item.parentPage)
        str += item.parentPage.name + delim;

    return str;

};


/**
 * Returns index of obj in arr.
 * Returns -1 if not found.
 * @param {any} obj
 * @param {Array} arr
 * @returns {Number}
 */
function indexOf(obj, arr) {
    for (var i = 0; i < arr.length; i++)
        if (arr[i] === obj)
            return i;
    return -1;
};


/**
 * Collects array of layer names from linked pdf items.
 * @param {Array<PageItem>} items - the linked pdfs.
 * @returns {Array<String>}
 */
function getLayerNames(items) {

    var layerNames = [],
        already = {};

    for (var i = 0; i < items.length; i++)
        for (var j = 0; j < items[i].graphicLayerOptions.graphicLayers.length; j++)
            if (!already[items[i].graphicLayerOptions.graphicLayers[j].name]) {
                layerNames.push(items[i].graphicLayerOptions.graphicLayers[j].name);
                already[items[i].graphicLayerOptions.graphicLayers[j].name] = true;
            }

    return layerNames;

};


/**
 * Shows UI for selecting layers of document.
 * @author m1b
 * @version 2023-06-09
 * @param {Object} settings
 * @param {Document} settings.doc - an Indesign Document.
 */
function chooseLayers(settings) {

    var layerNames = settings.layerNames,
        selectedLayerNames = [],
        w = new Window("dialog { text:'Set Layer Visibility' }"),
        layersListBox = w.add("ListBox {alignment:['fill','fill'], properties:{multiselect:true, showHeaders:true, numberOfColumns:1, columnTitles:['Layer'] } }"),
        buttons = w.add("Group { orientation: 'row', alignment: ['right','bottom'], margins:[0,12,0,22] }"),
        cancelButton = buttons.add("Button { text:'Cancel', properties: { name:'cancel' } }"),
        okButton = buttons.add("Button { text:'Set Visibility', properties: { name:'ok' } }");

    // populate layers menu
    for (var i = 0; i < layerNames.length; i++)
        layersListBox.add('item', layerNames[i]);

    /**
     * Updates `selectedLayerNames`
     * when listbox selection changes.
     */
    layersListBox.onChange = function () {

        var sel = this.selection;
        selectedLayerNames = [];
        settings.layers = [];

        if (sel == null)
            return;

        for (var i = 0; i < sel.length; i++)
            selectedLayerNames.push(sel[i].text);

    };

    // set the initial selection
    var sel = [];
    for (var i = 0; i < settings.visibleLayerNames.length; i++) {
        var index = indexOf(settings.visibleLayerNames[i], settings.layerNames);
        if (index != -1)
            sel.push(index);
    }
    layersListBox.selection = sel;
    layersListBox.maximumSize.height = w.maximumSize.height - 200;;
    layersListBox.active = true;

    if (w.show() == 2)
        // user cancelled
        return false;

    // add the selected layers to settings object
    settings.visibleLayerNames = selectedLayerNames;

};

 

Edit 2023-06-24: added some error checking.

suep89793525
Known Participant
June 23, 2023

Mark--Thank you so much!  I tested your script on my three-inset sample page and so far it works perfectly!  I'm going to try some other tests with AI and PDF files since our parent documents (spec sheets) also include these elements. My confidence level is high...but I'll defintely let you know if I run into any bugs along the way. Assuming all goes well, you have just saved me and my co-workers a tremendous amount of time and effort. Wow.