Create a table of contents (TOC) in Keynote from titles

Hello
I’m a total newbie to both Apple scripts and MacScripter, so please forgive any faux pas. (I have tried to follow the posting guidelines).

My desired use case
I create long reports and proposals for my company in Keynote (I know it’s not meant for this purpose, but it’s what my boss wants to use, so I have to).
I would like to create a script which pulls the title of each slide and lists them in the results pane, so that I can copy that list into my Keynote file, basically creating a table of contents for the document. If it could include slide numbers and hyperlink to them that would be amazing too, but just a text list would be a huge help.

What I’ve done
I came across this article http://gingerbbm.com/2012/08/keynote-table-of-contents/ and tried to adapt his script to pull out titles instead. But it doesn’t work, and frankly I have no idea why!

set mypath to (path to home folder as text) & "test.key"
set mynotes to ""
tell application "Keynote"
	open mypath
	tell slideshow 1
		set countslides to count of slides
		repeat with i from 1 to countslides by 1
			-- Only consider those slides with titles
			if (title of slide i) is not "" then
				-- Get the titles
				set mynotes to mynotes & titles of slide i & "	" & i & "
"
			end if
		end repeat
	end tell
	-- Write to the Result pane
	get mynotes
end tell

I have followed a few tutorials, but I’m at a loss.
Can anyone tell me where I’ve gone wrong? (in really simple lanuage?!)

Many thanks

I’ve some wrote TOC generating script and use them.

(1)Gather every title and make Numbers table

(2)Gather every title and make TOC page on Keynote document

(3)Export PDF from Keynote and generate PDF TOC

(4)Helper script for (2) make every back-link to each slide from TOC

Demonstration movie is here
https://www.youtube.com/watch?v=94-bHE5B2OI

Hi.

Either of these seem to work for getting the slide numbers and titles. The second’s probably slightly faster than the first:

set mypath to (path to home folder as text) & "test.key"
tell application "Keynote"
	set myDoc to (open file mypath) -- NB. 'file'.
	tell myDoc
		set {slideNumbers, slideTitles} to {slide number, object text of default title item} of ¬
			(slides where (object text of default title item is not ""))
	end tell
end tell

set mynotes to {"TOC:"}
repeat with i from 1 to (count slideNumbers)
	set end of mynotes to ((item i of slideNumbers) as text) & ". " & item i of slideTitles
end repeat

return mynotes
set mypath to (path to home folder as text) & "test.key"
tell application "Keynote"
	set myDoc to (open file mypath) -- NB. 'file'.
	tell myDoc
		set {slideNumbers, slideTitles} to {slide number, object text of default title item} of slides
	end tell
end tell

set mynotes to {"TOC:"}
repeat with i from 1 to (count slideNumbers)
	set thisTitle to item i of slideTitles
	if (thisTitle is not "") then
		set end of mynotes to ((item i of slideNumbers) as text) & ". " & thisTitle
	end if
end repeat

return mynotes

The returned list could be coerced directly to text with linefeed delimiters, but if you’re going to want to add hyperlinks, my guess is that you’d need to script the creation of the TOC slide with a separate container for each entry and then add the links to the containers manually.

Thank you all for your replies and help!

@Maro I’m very impressed that you can write these scripts in a different language to your mother tongue! Are your scripts available publicly?

@Nigel – I feel very stupid asking this, but I don’t think I’m adding the path or filename correctly as it keeps telling me that the file doesn’t exist. Would you mind telling me whether the way I’ve added it below is correct? Do I need to add the filename to line 3 too?

set mypath to "/Users/mac/Documents/" & "test.key"
tell application "Keynote"
	set myDoc to (open file mypath) -- NB. 'file'.
	tell myDoc
		set {slideNumbers, slideTitles} to {slide number, object text of default title item} of slides
	end tell
end tell

set mynotes to {"TOC:"}
repeat with i from 1 to (count slideNumbers)
	set thisTitle to item i of slideTitles
	if (thisTitle is not "") then
		set end of mynotes to ((item i of slideNumbers) as text) & ". " & thisTitle
	end if
end repeat

return mynotes

Thank you again.

I’ll sell it on Mac App Store. Like this.

https://apps.apple.com/us/app/kamenoko/id1507144869

(This app is full written in AppleScript)

Hi KittyO.

If the file’s in your Documents folder, the ‘path to’ line should be:

set mypath to (path to documents folder as text) & "test.key"

Nigel Garvey wrote:

Ah ha! So perhaps where I’m complicating things is that the file is in my icloud drive:
/Users/mac/Library/Mobile Documents/com~apple~Keynote/Documents/
So in that case would I need to include the full path as above or can I just refer to “icloud drive” or “folder”?

Sorry for my really basic questions – i am very very new to all this, but I hugely appreciate your help!

Hi KittyO.

I don’t have an iCloud account so I can’t test that situation.

On my Mojave machine, Keynote 10.1 still understands POSIX paths, so if the path you gave gets the file, if could be as simple as:

set mypath to "/Users/mac/Library/Mobile Documents/com~apple~Keynote/Documents/" & "test.key"
tell application "Keynote"
	open mypath -- Using the POSIX path directly.
	
	-- etc.
end tell

More generally:

set mypath to (path to library folder from user domain as text) & "Mobile Documents:com~apple~Keynote:Documents:" & "test.key"

tell application "Keynote"
	open file mypath -- Using a file specifier.
	
	-- etc.
end tell

That worked! Thank you so much for your help, I really appreciate it. This will save me hours and hours of work.

Nigel Garvey wrote:

Hi Fredrik71.

I must admit, I didn’t actually understand what you were saying. :slight_smile: There are no ‘text items’ in KittyO’s original script.

Looking again at Keynote’s dictionary, a ‘text item’ in that context is an iWork text container: an element, of which there may be one or several in a document. So if a script uses the iWork ‘text item’ term, it should really specify a particular or every ‘text item’, not just the class name.

tell application "Keynote"
	set theDocument to front document
	---
	tell theDocument to tell every slide
		object text of text items
		object text of every text item
		object text of text item 1
	end tell
end tell

I don’t know why ‘text item’ is acting as its own plural in your code. :confused:

As you say, ‘default title item’ and ‘default body item’ are document properties whose values are particular iWork containers. However, these containers are ‘shapes’, according to the dictionary, which seems slightly ridiculous. The properties listed for ‘shapes’ are exactly the same as those for ‘text items’, so you’d think they were just the same thing under another name in Keynote. But in fact substituting ‘shape’ for ‘text item’ in the above script returns no text at all, so the dictionary itself must be wrong.

With regard to the slides’ “Appearance” settings, they only govern whether the Titles, Bodies, and Slide Numbers can be seen or not. They don’t remove them from the document (otherwise the items wouldn’t reappear when re-enabled). It looks as if a script might be able to tell if these items are hidden by looking at their ‘position’, ‘width’, and/or ‘height’ properties, which I’m seeing as all zero values when the items are hidden.