<div dir="ltr"><div class="gmail_default" style="font-family:verdana,sans-serif;color:#000000">Hello everyone,</div><div class="gmail_default" style="font-family:verdana,sans-serif;color:#000000"><br></div><div class="gmail_default" style="font-family:verdana,sans-serif;color:#000000">I have been using IPE to make my presentation for many years now but I find it sometimes a bit limited when I want to include videos (or background animations made with manim: <a href="https://docs.manim.community/en/stable/tutorials/quickstart.html">https://docs.manim.community/en/stable/tutorials/quickstart.html</a>). I therefore tried to switch to reveal.js paired with quarto (<a href="https://quarto.org/docs/presentations/revealjs/">https://quarto.org/docs/presentations/revealjs/</a>). </div><div class="gmail_default" style="font-family:verdana,sans-serif;color:#000000">On the positive side it looks really good and it is pretty easy to create basic slides but, as often with such a solution, it is very rigid when it comes to add drawings or plots. </div><div class="gmail_default" style="font-family:verdana,sans-serif;color:#000000"><br></div><div class="gmail_default" style="font-family:verdana,sans-serif;color:#000000">I did, with a lot of help from chatGPT, a small script to convert slides made with IPE to svg images that are then loaded in a quarto file. It is working ok but I wanted to know if other people did similar things here and probably in a better way I did. For example now I save each slide separately in a SVG format which is a bit stupid when often each of these slides have 90% similar content. </div><div class="gmail_default" style="font-family:verdana,sans-serif;color:#000000"><br></div><div class="gmail_default" style="font-family:verdana,sans-serif;color:#000000">Here is my code:</div><div class="gmail_default" style="font-family:verdana,sans-serif;color:#000000"><br></div><div class="gmail_default" style="font-family:verdana,sans-serif;color:#000000">import os<br>import subprocess<br>import xml.etree.ElementTree as ET<br><br># ==============================<br># 🔹 User Configuration 🔹<br># ==============================<br><br># Path to your Ipe presentation file (.ipe)<br>IPE_FILE = "your_presentation.ipe"  # Change this to your actual file<br><br># Output folder for generated SVGs and markdown<br>OUTPUT_FOLDER = "output"<br><br># Name of the output Quarto markdown file<br>MARKDOWN_FILE = "slides.qmd"<br><br># Path to the Ipe rendering tool (iperender)<br>IPE_RENDER_PATH = "C:/path/to/iperender.exe"  # Change this to your actual iperender path<br><br># ==============================<br># ✅ Script Execution Begins<br># ==============================<br><br># Step 1: Verify the IPE file exists<br>if not os.path.exists(IPE_FILE):<br>    print(f"❌ ERROR: IPE file not found: {IPE_FILE}")<br>    exit(1)<br><br># Step 2: Create output directory<br>os.makedirs(OUTPUT_FOLDER, exist_ok=True)<br><br># Step 3: Parse the IPE XML file to get pages (slides) and their views<br>print(f"🔍 Extracting pages and views from {IPE_FILE}...")<br><br>tree = ET.parse(IPE_FILE)<br>root = tree.getroot()<br><br>slide_views = []  # List to store all slide titles and their views<br><br>for page_idx, page in enumerate(root.findall(".//page"), start=1):<br>    # Get the slide title (default: "Slide X")<br>    title_element = page.find("title")<br>    slide_title = title_element.text.strip() if title_element is not None else f"Slide {page_idx}"<br><br>    views = []<br>    for view_idx, view in enumerate(page.findall("view"), start=1):<br>        layers = view.attrib.get("layers", "").split()<br>        if layers:<br>            views.append((view_idx, layers))<br><br>    if views:<br>        slide_views.append((slide_title, page_idx, views))<br><br>print(f"✅ Found {len(slide_views)} slides with views.")<br><br># Step 4: Render each view of each slide<br>svg_files = []<br><br>for slide_title, page_idx, views in slide_views:<br>    for view_idx, (view_number, layers) in enumerate(views, start=1):<br>        output_svg = os.path.join(OUTPUT_FOLDER, f"{slide_title.replace(' ', '_')}_view_{view_idx}.svg")<br>        output_svg = output_svg.replace("\\", "/")  # Ensure cross-platform compatibility<br><br>        print(f"🎨 Rendering {slide_title} (Page {page_idx}, View {view_idx}) -> {output_svg}...")<br><br>        # Render the slide view directly from the IPE file<br>        render_cmd = [IPE_RENDER_PATH, "-svg", "-page", str(page_idx), "-view", str(view_number), IPE_FILE, output_svg]<br>        result = subprocess.run(render_cmd, capture_output=True, text=True)<br><br>        if result.returncode != 0 or not os.path.exists(output_svg):<br>            print(f"❌ ERROR: Failed to render {slide_title} (Page {page_idx}, View {view_idx}). Message:\n{result.stderr}")<br>            continue<br><br>        svg_files.append((slide_title, output_svg))<br><br># Step 5: Check if any SVGs were generated<br>if not svg_files:<br>    print("❌ ERROR: No slides generated. Check iperender and views.")<br>    exit(1)<br><br>print(f"✅ Generated {len(svg_files)} slides.")<br><br># Step 6: Generate Quarto/Reveal.js Markdown file with the "clean" theme<br>with open(MARKDOWN_FILE, "w") as f:<br>    f.write(<br>        """---<br>title: "My Presentation"<br>format: revealjs<br>revealjs:<br>  theme: [default, clean]  # Using the "clean" theme for better styling<br>  transition: fade<br>  slideNumber: true<br>  chalkboard: true<br>  previewLinks: true<br>  highlightStyle: github<br>---<br>"""<br>    )<br><br>    for slide_title, svg in svg_files:<br>        svg = svg.replace("\\", "/")  # Ensure paths are cross-platform<br>        f.write(f"## {slide_title}\n\n")  # Each view appears as a separate slide with the same title<br>        f.write(f"![]({svg}){{width=100%}}\n\n")<br><br>print(f"\n✅ Quarto/Reveal.js presentation generated: {MARKDOWN_FILE}")<br></div></div>