Automatically Replacing Helvetica Fonts in Adobe InDesign with a JavaScript Script

As of January 2023, Type1 Helvetica fonts are no longer usable in Adobe InDesign... 

This has been a headache for the company I work for.

To mitigate this problem, I created a JavaScript script that automates the process of replacing Helvetica fonts with Arial fonts in Adobe InDesign documents.

#target indesign

(function () {
    // Helvetica → Arial mapping
    var FONT_MAP = {
        "Helvetica\tMedium": "Arial\tRegular",
        "Helvetica\tBold": "Arial\tBold",
        "Helvetica\tBold Oblique": "Arial\tBold Italic"
    };

    if (app.documents.length === 0) {
        alert("No InDesign document is open.");
        return;
    }

    /*
     * If multiple documents are open:
     *   OK     = process all open documents
     *   Cancel = process only the active document
     */
    var documentsToProcess = [];

    if (app.documents.length > 1) {
        var processAll = confirm(
            "More than one document is open.\r\r" +
            "Click OK to process all open documents.\r" +
            "Click Cancel to process only the active document."
        );

        if (processAll) {
            for (var i = 0; i < app.documents.length; i++) {
                documentsToProcess.push(app.documents[i]);
            }
        } else {
            documentsToProcess.push(app.activeDocument);
        }
    } else {
        documentsToProcess.push(app.activeDocument);
    }

    // Make sure that all replacement fonts are available before editing.
    var missingReplacementFonts = getMissingReplacementFonts();

    if (missingReplacementFonts.length > 0) {
        alert(
            "The script cannot continue because the following replacement " +
            "fonts are not available:\r\r" +
            missingReplacementFonts.join("\r") +
            "\r\rInstall or activate these fonts and run the script again."
        );
        return;
    }

    var proceed = confirm(
        "This script will replace selected Helvetica fonts with Arial in:\r\r" +
        getDocumentNames(documentsToProcess).join("\r") +
        "\r\rThe documents will not be saved automatically.\r" +
        "It is recommended that you work on backup copies.\r\r" +
        "Continue?"
    );

    if (!proceed) {
        return;
    }

    app.doScript(
        function () {
            var result = {
                paragraphStylesChanged: 0,
                characterStylesChanged: 0,
                unsupportedHelveticaStyles: [],
                errors: []
            };

            for (var d = 0; d < documentsToProcess.length; d++) {
                processDocument(documentsToProcess[d], result);
            }

            showReport(result);
        },
        ScriptLanguage.JAVASCRIPT,
        undefined,
        UndoModes.ENTIRE_SCRIPT,
        "Replace Helvetica styles with Arial"
    );

    function processDocument(doc, result) {
        /*
         * These collections include styles inside style groups.
         */
        var paragraphStyles = doc.allParagraphStyles;
        var characterStyles = doc.allCharacterStyles;

        processStyles(
            doc,
            paragraphStyles,
            "Paragraph style",
            "paragraphStylesChanged",
            result
        );

        processStyles(
            doc,
            characterStyles,
            "Character style",
            "characterStylesChanged",
            result
        );
    }

    function processStyles(doc, styles, styleType, counterName, result) {
        for (var i = 0; i < styles.length; i++) {
            var style = styles[i];

            try {
                if (!style.isValid) {
                    continue;
                }

                var sourceFontName = getAppliedFontName(style);

                // A blank value normally means that the style inherits its font.
                if (sourceFontName === "") {
                    continue;
                }

                if (FONT_MAP[sourceFontName]) {
                    var replacementFont =
                        app.fonts.itemByName(FONT_MAP[sourceFontName]);

                    style.appliedFont = replacementFont;
                    result[counterName]++;

                } else if (isHelveticaFont(sourceFontName)) {
                    addUnique(
                        result.unsupportedHelveticaStyles,
                        doc.name + " | " +
                        styleType + ": " +
                        getStylePath(style) + " | " +
                        sourceFontName.replace("\t", " — ")
                    );
                }

            } catch (error) {
                result.errors.push(
                    doc.name + " | " +
                    styleType + ": " +
                    getStylePath(style) + " | " +
                    error.message
                );
            }
        }
    }

    function getAppliedFontName(style) {
        try {
            var appliedFont = style.appliedFont;

            if (
                appliedFont === null ||
                appliedFont === undefined ||
                appliedFont === NothingEnum.NOTHING
            ) {
                return "";
            }

            /*
             * appliedFont can return either a Font object or a string.
             */
            var fontName;

            if (typeof appliedFont === "string") {
                fontName = appliedFont;
            } else if (appliedFont.isValid) {
                fontName = appliedFont.name;
            } else {
                return "";
            }

            /*
             * A Font object's name normally contains:
             * family name + tab + style name.
             *
             * If only the family name is returned, append fontStyle.
             */
            if (fontName.indexOf("\t") === -1) {
                var fontStyle = getFontStyleName(style);

                if (fontStyle !== "") {
                    fontName += "\t" + fontStyle;
                }
            }

            return fontName;

        } catch (error) {
            return "";
        }
    }

    function getFontStyleName(style) {
        try {
            var fontStyle = style.fontStyle;

            if (
                fontStyle === null ||
                fontStyle === undefined ||
                fontStyle === NothingEnum.NOTHING
            ) {
                return "";
            }

            return String(fontStyle);

        } catch (error) {
            return "";
        }
    }

    function isHelveticaFont(fontName) {
        return (
            fontName === "Helvetica" ||
            fontName.indexOf("Helvetica\t") === 0
        );
    }

    function getMissingReplacementFonts() {
        var missing = [];

        for (var sourceFont in FONT_MAP) {
            if (!FONT_MAP.hasOwnProperty(sourceFont)) {
                continue;
            }

            var replacementName = FONT_MAP[sourceFont];
            var replacementFont = app.fonts.itemByName(replacementName);

            if (!replacementFont.isValid) {
                addUnique(missing, replacementName.replace("\t", " — "));
            }
        }

        return missing;
    }

    function getStylePath(style) {
        var names = [style.name];
        var parent = style.parent;

        while (
            parent &&
            parent.isValid &&
            parent.constructor &&
            (
                parent.constructor.name === "ParagraphStyleGroup" ||
                parent.constructor.name === "CharacterStyleGroup"
            )
        ) {
            names.unshift(parent.name);
            parent = parent.parent;
        }

        return names.join(" > ");
    }

    function getDocumentNames(documents) {
        var names = [];

        for (var i = 0; i < documents.length; i++) {
            names.push(documents[i].name);
        }

        return names;
    }

    function addUnique(array, value) {
        for (var i = 0; i < array.length; i++) {
            if (array[i] === value) {
                return;
            }
        }

        array.push(value);
    }

    function showReport(result) {
        var message =
            "Font replacement completed.\r\r" +
            "Paragraph styles changed: " +
            result.paragraphStylesChanged + "\r" +
            "Character styles changed: " +
            result.characterStylesChanged;

        if (result.unsupportedHelveticaStyles.length > 0) {
            message +=
                "\r\rHelvetica styles found but not replaced:\r" +
                result.unsupportedHelveticaStyles.join("\r");
        }

        if (result.errors.length > 0) {
            message +=
                "\r\rErrors:\r" +
                result.errors.join("\r");
        }

        message +=
            "\r\rThe documents have not been saved automatically." +
            "\rCheck text reflow and overset text before saving.";

        alert(message);
    }
})();

The script operates by checking if more than one document is open in Adobe InDesign and asking the user if they want to apply font changes to all open documents. Depending on the user’s choice, it then iterates through each document, replacing instances of Helvetica Medium, Helvetica Bold, and Helvetica Bold Oblique with their Arial counterparts.

Share: X Email