javascriptapplescriptautomatorjavascript-automationkeynote

Replace fonts in Keynote with JXA


I'm trying to find/replace problematic fonts in Keynote, and found this script to change fonts using JXA, but need to scan every character and text object for the offending font.

I've tried this to start, but the conditional isn't working. Help!

PROBLEM_FONT = 'Arial'
REPLACEMENT_FONT = 'Helvetica'

document = Application('Keynote').documents[0]
for slide in document.slides
  for textItem in slide.textItems
    if textItem.objectText.font == PROBLEM_FONT
        textItem.objectText.font = REPLACEMENT_FONT

Solution

  • The GitHub gist you referred to is written in CoffeeScript, not JavaScript. It needs to be compiled (converted) to JavaScript. Your script, which is an extract of the other script, is also written in CoffeeScript.

    Here's a quick vanilla JS version of the script:

    var PROBLEM_FONT = 'Arial';
    var REPLACEMENT_FONT = 'Helvetica';
    var slides = Application('Keynote').documents[0].slides;
    
    for (var i = 0; i < slides.length; i++) {
        var slide = slides[i];
        for (var j = 0; j < slide.textItems.length; j++) {
            var textItem = slide.textItems[j];
            if (textItem.objectText.font === PROBLEM_FONT) {
                textItem.objectText.font = REPLACEMENT_FONT;
            }
        }
    }
    

    Important note: The font name for Arial is different 'under the hood'; for example, italicized Arial is listed as "Arial-ItalicMT". Your script is not taking this into account, which may be why the conditional statement fails.

    You may want to add a substring check like so:

    (untested, but hopefully you get the idea)

    var PROBLEM_FONT = 'Arial';
    var REPLACEMENT_FONT = 'Helvetica';
    var slides = Application('Keynote').documents[0].slides;
    
    for (var i = 0; i < slides.length; i++) {
        var slide = slides[i];
        for (var j = 0; j < slide.textItems.length; j++) {
            var textItem = slide.textItems[j];
            if (textItem.objectText.font.indexOf(PROBLEM_FONT) !== -1) {
                textItem.objectText.font = REPLACEMENT_FONT;
            }
        }
    }
    

    In case it helps, here is a pure AppleScript version, which includes a substring check for the font name. I have successfully tested this with Keynote 7.0.5 on macOS Sierra.

    tell application "Keynote"
    
        if (front document exists) then
            tell every slide of front document
    
                -- Targeting body text
                tell every text item
                    if (the font of its object text as text) contains "Arial" then
                        set the font of its object text to "Monaco"
                    end if
                end tell
    
                -- Targeting slide titles
                tell default title item
                    if (the font of its object text as text) contains "Arial" then
                        set the font of its object text to "Monaco"
                    end if
                end tell
    
                -- Targeting text boxes within groups
                tell every group
    
                    tell every text item
                        if (the font of its object text as text) contains "Arial" then
                            set the font of its object text to "Monaco"
                        end if
                    end tell
    
                end tell
    
            end tell
        end if
    
    end tell
    

    (I used Monaco for this example so that you can quickly tell if the script worked, since Helvetica and Arial are difficult to distinguish.)