Skip to main content
Participant
April 24, 2025
Question

Image Alt-Text Script - "Custom" Source Issue

  • April 24, 2025
  • 4 replies
  • 487 views

I've been working on a script to loop through all the images in a document and set their alt text. The issue I'm running into is that even though the custom alt text is being applied, the "Object Export Options" for those images aren't being set to "Custom." As a result, when I export to PDF, the alt text doesn't appear unless I manually go in and set each image’s export option to "Custom."

I'm looking for a way to programmatically set both the custom alt text and ensure that the "Alt Text Source" in the Object Export Options is set to "Custom," so the PDF export includes the alt text correctly.
Also if I check decorative I need that to display properly in the Object Export Options as well. and that isnt happening.

Any help would be greatly appreciated! My current code is below:

 

// Image Alt Text Assistant
// This script loops through all images in the InDesign document and
// prompts the user to add alt text or mark images as decorative

// Main function
function main() {
    if (app.documents.length === 0) {
        alert("Please open a document before running this script.");
        return;
    }
    
    var doc = app.activeDocument;
    var allGraphics = getAllGraphics(doc);
    
    if (allGraphics.length === 0) {
        alert("No images found in the document.");
        return;
    }
    
    // Ask the user if they want to process all images
    var startScript = confirm("Alt Text Assistant will process " + allGraphics.length + " images.\nClick Yes to begin or No to exit.");
    if (!startScript) {
        return; // Exit the script if the user clicks No
    }
    
    processGraphics(allGraphics);
}

// Find all graphics in the document, including those in groups and nested frames
function getAllGraphics(doc) {
    var allGraphics = [];
    
    // Process all pages in the document
    for (var p = 0; p < doc.pages.length; p++) {
        var page = doc.pages[p];
        
        // Process all page items on each page
        for (var i = 0; i < page.allPageItems.length; i++) {
            var item = page.allPageItems[i];
            
            // Check if the item is a graphic or image
            if ((item instanceof Rectangle || 
                 item instanceof Oval || 
                 item instanceof Polygon) && 
                item.graphics.length > 0) {
                allGraphics.push(item);
            }
        }
    }
    
    return allGraphics;
}

// Process each graphic and prompt for alt text
function processGraphics(graphics) {
    var skippedCount = 0;
    var updatedCount = 0;
    var decorativeCount = 0;
    var processedCount = 0;
    
    // Save the current view state to restore later
    var originalPage = app.activeWindow.activePage;
    
    for (var i = 0; i < graphics.length; i++) {
        var graphic = graphics[i];
        var currentAltText = "";
        var isDecorative = false;
        
        // Check if the graphic already has alt text
        try {
            currentAltText = graphic.objectExportOptions.customAltText || "";
        } catch (e) {
            // Error handling if properties can't be accessed
        }
        
        // Navigate to the page containing the image and select it
        try {
            // Go to the page with the image
            app.activeWindow.activePage = graphic.parentPage;
            
            // Select just this frame to highlight it
            app.selection = [];
            graphic.select();
            
            // Pause briefly to let InDesign update the display
            app.scriptPreferences.enableRedraw = true;
            $.sleep(100);
        } catch (e) {
            // Error handling if we can't navigate to the image
            alert("Could not navigate to the image (but will continue): " + e);
        }
        
        // Create a preview of the graphic location for the user
        var locationInfo = "Page " + graphic.parentPage.name;
        
        // Create dialog using the ScriptUI approach instead of Dialog API
        var dialog = new Window('dialog', "Alt Text Assistant - Image " + (i+1) + " of " + graphics.length);
        dialog.orientation = 'column';
        dialog.alignChildren = ['fill', 'top'];
        dialog.spacing = 10;
        dialog.margins = 16;
        
        // Image information section
        var infoGroup = dialog.add('panel', undefined, 'Image Information');
        infoGroup.orientation = 'column';
        infoGroup.alignChildren = ['left', 'top'];
        infoGroup.margins = [10, 15, 10, 10];
        
        infoGroup.add('statictext', undefined, "Image Location: " + locationInfo);
        
        // Add image file name if available
        try {
            if (graphic.graphics.length > 0 && graphic.graphics[0].itemLink) {
                var fileName = graphic.graphics[0].itemLink.name;
                infoGroup.add('statictext', undefined, "File: " + fileName);
            }
        } catch (e) {
            // Silently ignore errors getting filename
        }
        
        // Current alt text display (if any)
        if (currentAltText) {
            infoGroup.add('statictext', undefined, "Current Alt Text: " + currentAltText);
        }
        
        // Alt text input section
        var altTextGroup = dialog.add('panel', undefined, 'Alternative Text');
        altTextGroup.orientation = 'column';
        altTextGroup.alignChildren = ['fill', 'top'];
        altTextGroup.margins = [10, 15, 10, 10];
        
        altTextGroup.add('statictext', undefined, "Enter alternative text for this image:");
        var altTextEntry = altTextGroup.add('edittext', undefined, currentAltText, {multiline: true});
        altTextEntry.preferredSize.width = 350;
        altTextEntry.preferredSize.height = 100;
        
        // Decorative checkbox
        var decorativeCheckbox = altTextGroup.add('checkbox', undefined, "This image is decorative (no alt text needed)");
        decorativeCheckbox.value = isDecorative;
        
        // Set up checkbox behavior - disable text field when checked
        decorativeCheckbox.onClick = function() {
            altTextEntry.enabled = !this.value;
            if (this.value) {
                altTextEntry.text = "";
            }
        };
        
        // Help text
        dialog.add('statictext', undefined, "The image is currently selected on the page");
        
        // Button group
        var buttonGroup = dialog.add('group');
        buttonGroup.orientation = 'row';
        buttonGroup.alignChildren = ['center', 'center'];
        
        var okButton = buttonGroup.add('button', undefined, 'OK', {name: 'ok'});
        var skipButton = buttonGroup.add('button', undefined, 'Skip');
        var exitButton = buttonGroup.add('button', undefined, 'Exit Script');
        
        // Set button behavior
        skipButton.onClick = function() {
            skippedCount++;
            dialog.close(0); // Close with cancel code
        };
        
        exitButton.onClick = function() {
            var confirmExit = confirm("Exit the Alt Text Assistant?\nYour progress so far will be saved.");
            if (confirmExit) {
                i = graphics.length; // Force exit from the loop
                dialog.close(0);
            }
        };
        
        // Show the dialog
        if (dialog.show() == 1) { // 1 = OK button
            // User clicked OK
            var newAltText = altTextEntry.text;
            var isNowDecorative = decorativeCheckbox.value;
            
            // Update the alt text
            try {
                if (isNowDecorative) {
                    graphic.objectExportOptions.customAltText = "";
                    decorativeCount++;
                } else if (newAltText) {
                    graphic.objectExportOptions.customAltText = newAltText;
                    updatedCount++;
                }
                
                // Also try to set the Alt Text Source to Custom
                try {
                    app.doScript(
                        "app.selection[0].objectExportOptions.exportOption = ExportOption.CUSTOM;",
                        ScriptLanguage.JAVASCRIPT
                    );
                } catch (e) {
                    // Silently fail if this property isn't available
                }
            } catch (e) {
                alert("Error setting alt text: " + e);
            }
        }
        
        processedCount++;
        
        // Check if the user exited early
        if (i >= graphics.length) {
            break;
        }
    }
    
    // Restore the original view state
    try {
        app.activeWindow.activePage = originalPage;
        app.selection = [];
    } catch (e) {
        // Silently ignore errors restoring view
    }
    
    // Show summary of changes without any reminders about manually setting options
    alert("Alt Text Assistant complete!\n\n" +
          "Images processed: " + processedCount + " of " + graphics.length + "\n" +
          "Updated with alt text: " + updatedCount + "\n" +
          "Marked as decorative: " + decorativeCount + "\n" +
          "Skipped: " + skippedCount);
}

// Run the script
main();

 

4 replies

Robert at ID-Tasker
Legend
April 24, 2025
Community Expert
April 24, 2025

Hi @Bdemmler ,

there is no need to check all pageItems of a document.

You just have to loop the allGraphics array of the document.

 

More tomorrow…

 

Regards,
Uwe Laubender
( Adobe Community Expert )

Mike Witherell
Community Expert
Community Expert
April 24, 2025

Recently this script that checks accessibility was added by Keith Gilbert

https://gilbertconsulting.com/scripts

Maybe he can help you with your script?

Mike Witherell
BdemmlerAuthor
Participant
April 24, 2025