hi all
with a bit of help from ChatGPT, I created this script to insert an Outline slide in my presentation, using the text from my ‘Divider’ slides as the outline sections. This is for longer slideshows (eg lecture slides) where you have lots of slides in each section, and you don’t want headings in your outline for every single slide.
for this to work, you need to have a master slide called ‘Content’ and another master slide called ‘Divider’. The outline slide has hyperlinks to the corresponding Divider slides. (A further refinement would be to add ‘back to outline’ links on the divider slides!)
hope this is useful for others. I note that the debug output gets sent to a temp file (/tmp/outline_debug.log). If anyone has a better way of getting debug output from a python uno script, please let me know! I tested this in LO 24.2 on Ubuntu 24.04.
cheers
JP
# ~/.config/libreoffice/4/user/Scripts/python/OutlineFromDividers.py
import uno, os
from com.sun.star.text.ControlCharacter import PARAGRAPH_BREAK
LOG = "/tmp/outline_debug.log"
def log(line: str) -> None:
with open(LOG, "a", encoding="utf‑8") as f:
f.write(line + "\n")
def tag_and_outline_dividers():
# fresh log
try:
os.remove(LOG)
except FileNotFoundError:
pass
log("—— Outline macro run ——")
doc = XSCRIPTCONTEXT.getDocument()
pages = doc.getDrawPages()
masters = doc.getMasterPages()
base_url = doc.URL or ""
# 1. look for a master page called "Divider"
divider = next((m for m in masters if m.Name == "Divider"), None)
if not divider:
log("❌ No “Divider” master – aborting.")
return
log("✅ Found “Divider” master.")
# 2. collect every slide that already uses the Divider master
entries = []
for i in range(pages.getCount()):
slide = pages.getByIndex(i)
try:
if slide.getMasterPage().Name == "Divider":
title = next(
(shp.String for shp in slide
if shp.supportsService("com.sun.star.presentation.TitleTextShape")),
None)
if title:
title = " ".join(title.splitlines())
log(f" ▸ Divider title: {title} (currently slide {i+1})")
entries.append((i + 1, title))
except Exception:
pass
log(f"🔍 Found {len(entries)} divider slide(s).")
if not entries:
log("Nothing to outline – aborting.")
return
# 3. choose a layout for the outline slide
preferred = ["Title, Content", "Content", "Title"]
tc = None
for name in preferred:
tc = next((m for m in masters if m.Name == name), None)
if tc:
log(f"✅ Using “{name}” layout for the outline slide.")
break
if not tc:
log("⚠️ No suitable named layout – outline will be a blank slide.")
# 4. create the outline slide (insert at index 0 so it becomes slide 1)
outline = pages.insertNewByIndex(0)
# attach the “Content” master page found earlier
if tc:
outline.setMasterPage(tc)
# ── find a numeric layout ID that already gives Title+Content ──────────
def first_title_content_layout():
for i in range(1, pages.getCount()): # skip the new outline itself
s = pages.getByIndex(i)
has_title = any(
shp.supportsService("com.sun.star.presentation.PlaceholderShape")
and shp.PlaceholderType == 1
for shp in s)
has_content = any(
shp.supportsService("com.sun.star.presentation.PlaceholderShape")
and shp.PlaceholderType == 2
for shp in s)
if has_title and has_content:
return s.Layout # <- short integer
return None
layout_id = 2 #first_title_content_layout()
if layout_id is not None:
try:
outline.Layout = layout_id
log(f"✅ Applied existing Title+Content layout id {layout_id}.")
except Exception as e:
log(f"⚠️ Could not apply layout id {layout_id}: {e}")
else:
log("⚠️ No existing Title+Content slide found – will create placeholders.")
# 5. TITLE placeholder – look for TitleTextShape
title_shape = next(
(shp for shp in outline
if shp.supportsService("com.sun.star.presentation.TitleTextShape")),
None)
if title_shape is None:
# (extremely unlikely once Layout=2 is applied, but keep a fallback)
title_shape = doc.createInstance("com.sun.star.drawing.TextShape")
title_shape.setSize(uno.createUnoStruct("com.sun.star.awt.Size", 15000, 1500))
title_shape.setPosition(uno.createUnoStruct("com.sun.star.awt.Point", 1000, 200))
outline.add(title_shape)
title_shape.Text.setString("Outline")
# 6. CONTENT placeholder – look for OutlinerShape
content = next(
(shp for shp in outline
if shp.supportsService("com.sun.star.presentation.OutlinerShape")),
None)
if content is None:
log("ℹ️ No OutlinerShape found – creating a TextShape.")
content = doc.createInstance("com.sun.star.drawing.TextShape")
content.setSize(uno.createUnoStruct("com.sun.star.awt.Size", 15000, 10000))
content.setPosition(uno.createUnoStruct("com.sun.star.awt.Point", 1000, 1800))
outline.add(content)
# 7. bump every stored slide number by +1 (outline slide sits at the front)
entries = [(num + 1, title) for num, title in entries]
# 8. populate bullets + internal links
text, cursor = content.Text, content.Text.createTextCursor()
for num, title in entries:
url_field = doc.createInstance("com.sun.star.text.TextField.URL")
url_field.URL = f"#Slide {num}"
url_field.Representation = f"• {title}"
text.insertTextContent(cursor, url_field, False)
text.insertControlCharacter(
cursor,
uno.getConstantByName(
"com.sun.star.text.ControlCharacter.PARAGRAPH_BREAK"),
False)
log("✅ Populated outline slide.")
g_exportedScripts = tag_and_outline_dividers,
# vim: sw=4:et