3
votes

I wrote a database web app for handling church worship songs, and I'm trying to add a module to output selected songs for projecting the lyrics. I initially thought all my users were using Powerpoint, which can import a simple text file with each line preceded by 0-5 tabs (0-tab line becomes the title of a new slide, and one or more tabs means a "bullet point" with the level corresponding to how many tabs). So my module currently outputs such a text file, and the presenter would open a Powerpoint template styled the way they want for song presentation, insert the text file as an "outline" for new slides, and voila. Here is a small sample of the text file structure that works in Powerpoint (with one slide in Japanese with romanization in smaller font by utilizing the next level of outline):

As the Deer
    As the deer panteth for the water
    So my soul longeth after Thee
    You alone are my heart's desire
    And I long to worship Thee
As the Deer
    You alone are my strength, my shield
    To You alone may my spirit yield
    You alone are my heart's desire
    And I long to worship Thee
鹿のように (As the Deer)
    谷川の流れを したう鹿のように
        tanigawa no nagare o shitau shika no yoo ni
    主よ、わが魂 あなたをしたう
        shu yo, waga tamashii anata o shitau
鹿のように (As the Deer)
    あなたこそ わが盾、 あなたこそ わが力
        anata koso waga tate, anata koso waga chikara
    あなたこそ わが望み われは主をあおぐ
        anata koso waga nozomi, ware wa shu o aogu

But my church's pastor (whose computer is used for the projection) and the two other worship leaders besides me who select songs all use Macs. They always talked about slides for Powerpoint, so I assumed that's what they were using. But just as I finished the code to output tabbed text, I found out that when they say "Powerpoint" they really mean Keynote, which has no ability to use plain text files. Old versions of Keynote internally stored slide data in XML (https://www.xml.com/pub/a/2004/01/07/keynote.html), but newer Keynote versions use progressively more opaque formats (http://justsolve.archiveteam.org/wiki/IWork). Apple clearly doesn't want anything except Keynote creating or editing Keynote presentations.

So I'm looking for suggestions on how to approach this. I'd like a process that doesn't involve me creating a Powerpoint file for them to convert to Keynote. Not only should I not be in the loop every week, but there are some line spacing problems in the conversion.

I know how to use LaTeX to generate PDFs from the data (I currently only do it for printed chordsheets, but I'm sure I could figure out how to do slide-style layouts), but Keynote apparently only imports one page of a PDF at a time - a typical Sunday worship set would be around 30-50 slides, so that would be pretty annoying. Plus, I would like the worship leader/pastor to be able to adjust things in Keynote if necessary - if it comes from a PDF, that won't be possible. So my first choice is to somehow marry a Keynote "template" with text of some sort (XML, JSON, tabbed text, or whatever).

This conversation seemed to hint that it might be possible with Applescript (which sounds like the iWorks equivalent of Office's VBA), but since I don't own a Mac, that would require long hours of borrowing someone else's computer to learn the language and develop/test the script (unless it's simple enough that one of you is willing to whip up something for me). Ideas?


Edit: After CJK's answer and comments, I realized that an example of an end result might help visualize what I'm trying to do. Here are two slides from Powerpoint, choosing Japanese slide examples, because if I can get this to work, English slides are easy. When I tried to import this Powerpoint into Keynote a week ago, the most stubborn styling was the line spacing of the Japanese (level one "bullet") and romanization (level two).

(In case you're wondering, yes, that's the title box at the bottom - I know it's unconventional, but due to a low ceiling, the top half of our slides are prime real estate.)

enter image description here

On this next example, the non-seasonal version, I added something Powerpoint's text file import doesn't support, because CJK's approach inspired me to see how it could be done with rich text in the script: Two different text formats within the title box. (Previously I had planned to just put the copyright info on the last slide of each song, below the lyrics using level 3 or 4 of the "bullet" styles. But I like it better with the title.) enter image description here

Edit 2: Attempting to springboard from CJK's script, here is an attempt at code to use an existing master slide like "Title & Bullets" and paragraph styles (completely untested - just bits from examples online). I'll be able to test it in two days, but I'm putting it here so CJK can see what I've done so far:

-- *** I'd like to use relative path so it would be portable, but (path to home folder as text) gave errors ***
property SambiDBTextFile : "/Users/Rachel/Desktop/Songs.txt"

property masterSlideName : "Lyrics" -- custom master slide based on "Title & Bullets"

-- ** If I can use paragraph styles, I won't need these ***
property TextSizes : {32, 28, 20}
property TextColours : {"white", {63222,57568,41634}, {63222,57568,41634}}
property TextFonts : {"Hiragino Kaku Gothic Pro", "Arial Italic", "Hiragino Kaku Gothic Pro"}

-- ** This is what I really want to use, but I don't know if I can ***
property TextStyles : {"Main Lyrics", "Romaji Lyrics", "Song Credits"}

set AppleScript's text item delimiters to tab
set notes to paragraphs of (read SambiDBTextFile)

tell application "Keynote" to tell current document

    -- *** Check for master slide existence, and substitute if absent ***
    set masterSlideList to the name of every master slide
    if masterSlideName is not in masterSlideList then
        -- *** Create master slide? Nah, probably not possible ***
        display alert ("Master Slide") message "Master slide '" & masterSlideName & "' not found; using 'Title & Bullets' instead."
        set masterSlideName to "Title & Bullets"
    end if

    -- Create slides with content from Keynote text file
    repeat with i from 1 to number of notes
        if item i of notes is "" then exit repeat -- EOF

        -- Get the text (without tabstops) and the level of indentation
        set [TextContent, TabValue] to [last text item, number of rest of reverse of text items] of item i of notes

        if TabValue is 0 then -- Indicates title of new slide
            set current slide to make new slide with properties {base slide:master slide masterSlideName}
            set object text of the default title item to TextContent
        else -- TabValue is not 0, indicating lyrics
            if TabValue > 3 then set TabValue to 3

            -- *** I have no idea if this will work, but the point is to append
            set object text of default body item to object text of default body item & TextContent & return

            -- *** Style the line just added ***
            -- *** Plan A: use paragraph styles (not sure if I can do this) ***
            set paragraph style of paragraph ((count of paragraphs of default body item) - 1) of default body item to item TabValue of TextStyles

            -- *** Plan B: hardcoded styling (uncomment if above line doesn't work) ***
            --tell paragraph ((count of paragraphs of default body item) - 1) of default body item
                --set its color to item TabValue of TextColours
                --set its font to item TabValue of TextFonts
                --set its size to item TabValue of TextSizes
            --end tell
        end if
    end repeat
end tell
1

1 Answers

3
votes

Having played around a little bit with Keynote and AppleScript today, I think the following script will produce something along the lines of what you want. It includes the option to set different text settings based on the level of tabulation (1-5) of each line within the text file being parsed.

    property KeynoteTextFile : "/Users/CK/Desktop/Keynote.txt"

    property PresentationTitle : "My Presentation"
    property _W : 1024 -- The width of each slide
    property _H : 768 -- The Height of each slide

    -- Text properties for the cover title and each slide title
    property CoverTextStyle : {font:"Arial Bold", color:"white", size:96}
    property TitleTextStyle : {font:"Arial Bold", color:"white", size:48}

    -- Spacing above and below the title of each slide
    property TitleMargins : {top:30, bottom:100}
    -- Spacing between lines in the body of each slide
    property VerticalSpacing : 75

    -- Text properties for the body of each slide for
    -- each level of tabulation
    property Tabulations : {0.1, 0.2, 0.3, 0.4, 0.5}
    property TextSizes : {32, 28, 24, 20, 16}
    property TextColours : {"white", "blue", "green", "magenta", "orange"}
    property TextFonts : {"Arial", "Arial Italic", "Times New Roman Bold", ¬
        "Times New Roman Bold Italic", "Times New Roman Italic"}


    set AppleScript's text item delimiters to tab
    set notes to paragraphs of (read KeynoteTextFile)

    -- Create new presentation with cover slide
    tell application "Keynote" to tell (make new document with properties ¬
        {document theme:theme "Black", width:_W, height:_H})

        set MyPresentation to it

        set base slide of current slide to master slide "Blank"

        tell the first slide to ¬
            set CoverTitle to make new text item ¬
                with properties {object text:PresentationTitle}

        set properties of object text of the CoverTitle to CoverTextStyle
    end tell

    -- Create slides with content from Keynote text file
    repeat with i from 1 to number of notes
        if item i of notes is "" then exit repeat -- EOF

        -- Get the text (without tabstops)
        -- and the level of indentation
        set [TextContent, TabValue] to ¬
            [last text item, number of rest of reverse of text items] ¬
                of item i of notes

        if TabValue is 0 then -- Indicates title of new slide
            tell application "Keynote"

                tell (make new slide at end of slides of MyPresentation) to ¬
                    set Title to make new text item ¬
                        with properties {object text:TextContent}

                set properties of object text of the Title to TitleTextStyle
                copy position of Title to [_x, _y]
                set position of Title to [_x, |top| of TitleMargins]

            end tell
        else -- TabValue is not 0, indicating slide content
            if TabValue > 5 then set TabValue to 5

            tell application "Keynote" to tell current slide of MyPresentation
                set n to number of text items

                set T to make new text item with properties ¬
                    {object text:TextContent}

                tell object text of T
                    set its color to item TabValue of TextColours
                    set its font to item TabValue of TextFonts
                    set its size to item TabValue of TextSizes
                end tell

                set position of T to ¬
                    [(item TabValue of Tabulations) * _W, ¬
                        VerticalSpacing * n + (|bottom| of TitleMargins)]

            end tell
        end if
    end repeat

    -- Go to first slide of presentation and bring Keynote
    -- into the foreground
    tell application "Keynote"
        set current slide of MyPresentation to first slide of MyPresentation
        activate
    end tell

Here are some lines from my Keynote.txt file:

This is a Title
    This is indented by 1 tab
    So is this
        This is indented by 2 tabs
            This is 3 tabs
    Back to 1 tab

which produced this slide:

A Keynote slide made using AppleScript

Bear in mind that the level of indenting on the actual slide is determined, not by the tabs in the text file, but by the values of the property tabulations defined at the top of the script. Hence, the only effect the presence of a tab in the parsed text file will have will be to determine the set of characteristics to apply to the text when it's rendered on the slide (font, colour, size, and indent independent of its text file indent).

As a final note, I should point out that the lines in the text file can begin with 0-5 tabs. 6 or more tabs are treated as though there were just 5 tabs. However, importantly, the rest of the line should not contain any tabs within the text. As the script stands, this will produce some odd results. It's possible to adjust the script to cater for lines that need to contain tabs within the text, but I didn't see the need at present.