General

poster-presentation - Claude MCP Skill

Create scientific conference posters as native, editable .pptx files using python-pptx. Handles A0/A1 layouts, section placement, figure insertion, and academic color schemes. Exports editable .pptx and PDF. Use when the user wants a directly editable PowerPoint poster; for HTML/CSS-based posters exported to PDF/PPTX use pptx-posters, and for standard LaTeX posters use latex-posters.

SEO Guide: Enhance your AI agent with the poster-presentation tool. This Model Context Protocol (MCP) server allows Claude Desktop and other LLMs to create scientific conference posters as native, editable .pptx files using python-pptx. handles a0/a... Download and configure this skill to unlock new capabilities for your AI workflow.

🌟4 stars β€’ 255 forks
πŸ“₯0 downloads

Documentation

SKILL.md
# Scientific Conference Poster β€” PowerPoint (.pptx)

## Overview

This skill creates professional scientific conference posters as editable PowerPoint (.pptx) files using `python-pptx`. It supports standard conference poster sizes (A0, A1), landscape and portrait orientations, structured academic sections, figure insertion with captions, and publication-quality academic color schemes.

The generated `.pptx` is fully editable, allowing researchers to fine-tune layout, fonts, and colors using Microsoft PowerPoint, LibreOffice Impress, or any compatible application. PDF export is available via LibreOffice or PowerPoint.

---

## When to Use This Skill

Use this skill when:
- Creating a poster for an academic conference, symposium, or workshop
- Converting a research paper or manuscript into poster format
- Building a poster template for a research group or institution
- Presenting preliminary results, thesis work, or funded project outcomes
- Needing an editable (non-PDF) poster that collaborators can update

**Trigger phrases:**
- "Create a conference poster as a PowerPoint / .pptx"
- "Make me a scientific poster I can edit in PowerPoint"
- "Generate a poster for my paper / conference / symposium"
- "Build an A0 / A1 poster for [conference name]"
- "Create a research poster with sections for methods, results, conclusions"

---

## Prerequisites

Install required Python packages before running any poster generation code:

```bash
pip install python-pptx Pillow
```

For PDF export from the command line:

```bash
# macOS (via Homebrew)
brew install --cask libreoffice

# Ubuntu / Debian
sudo apt-get install libreoffice

# Windows β€” download from https://www.libreoffice.org/
```

---

## Standard Poster Dimensions

| Format   | Orientation | Width (cm) | Height (cm) | Width (in) | Height (in) | Common use          |
|----------|-------------|------------|-------------|------------|-------------|---------------------|
| A0       | Portrait    | 84.1       | 118.9       | 33.11      | 46.81       | European conferences|
| A0       | Landscape   | 118.9      | 84.1        | 46.81      | 33.11       | US / mixed format   |
| A1       | Portrait    | 59.4       | 84.1        | 23.39      | 33.11       | Smaller venues      |
| A1       | Landscape   | 84.1       | 59.4        | 33.11      | 23.39       | Departmental events |
| 36Γ—48 in | Portrait    | 91.44      | 121.92      | 36.0       | 48.0        | US conferences      |
| 48Γ—36 in | Landscape   | 121.92     | 91.44       | 48.0       | 36.0        | US conferences      |

**python-pptx uses EMUs (English Metric Units): 1 inch = 914400 EMU, 1 cm = 360000 EMU**

---

## Workflow Phases

### Phase 1: Gather Content

Collect all poster content from the user or source document:

1. **Title** β€” full paper/poster title
2. **Authors** β€” list with superscript affiliation numbers
3. **Affiliations** β€” institution names linked to authors
4. **Contact / corresponding author** email
5. **Abstract** β€” 150–250 words
6. **Introduction / Background** β€” key context and motivation (3–5 bullet points or short paragraphs)
7. **Methods** β€” concise description with optional workflow figure
8. **Results** β€” key findings, data visualizations, tables
9. **Conclusions** β€” 4–6 bullet points
10. **Acknowledgements** β€” funding sources, collaborators
11. **References** β€” 5–10 key references (abbreviated format)
12. **Figures** β€” file paths to images/plots to embed
13. **Logo(s)** β€” institutional/conference logo paths
14. **Color scheme** β€” preferred colors or institution palette (see schemes below)

### Phase 2: Plan Layout

Standard two- or three-column academic poster layout:

```
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  LOGO  β”‚           TITLE / AUTHORS / AFFILIATIONS         β”‚  LOGO β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  Introduction   β”‚   Methods      β”‚   Results              β”‚
β”‚                 β”‚                β”‚                        β”‚
β”‚  Background     β”‚   Workflow     β”‚   Figure 1             β”‚
β”‚                 β”‚   Figure       β”‚                        β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€   Figure 2             β”‚
β”‚  [Optional middle spanning row]  β”‚                        β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  Conclusions    β”‚ Acknowledgementsβ”‚  References            β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
```

### Phase 3: Generate .pptx

Use the code templates below to build the poster programmatically.

### Phase 4: Export

- Save as `.pptx` (primary deliverable β€” fully editable)
- Export to PDF via LibreOffice or PowerPoint (see export section)
- Verify layout at 100% zoom before delivering

---

## Core Code: Poster Foundation

```python
"""
scientific_poster.py β€” Generate A0 landscape conference poster as .pptx
Requires: python-pptx, Pillow
"""

from pptx import Presentation
from pptx.util import Inches, Pt, Emu, Cm
from pptx.dml.color import RGBColor
from pptx.enum.text import PP_ALIGN
from pptx.util import Inches, Pt
import os


# ── Dimensions ────────────────────────────────────────────────────────────────
# A0 Landscape: 118.9 cm Γ— 84.1 cm
POSTER_WIDTH_CM  = 118.9
POSTER_HEIGHT_CM = 84.1

def cm(value):
    """Convert centimetres to EMUs."""
    return Cm(value)


def create_poster(output_path: str = "poster.pptx") -> Presentation:
    """Create and return a blank poster Presentation at A0 landscape size."""
    prs = Presentation()
    prs.slide_width  = cm(POSTER_WIDTH_CM)
    prs.slide_height = cm(POSTER_HEIGHT_CM)

    # Remove all default placeholder layouts β€” use blank slide
    slide_layout = prs.slide_layouts[6]  # index 6 = blank
    slide = prs.slides.add_slide(slide_layout)

    return prs, slide
```

---

## Academic Color Schemes

```python
# ── Color Palettes ────────────────────────────────────────────────────────────

SCHEMES = {
    "classic_blue": {
        "header_bg":    RGBColor(0x1A, 0x3A, 0x5C),   # dark navy
        "header_text":  RGBColor(0xFF, 0xFF, 0xFF),   # white
        "section_bg":   RGBColor(0xD6, 0xE4, 0xF0),   # light blue
        "section_header": RGBColor(0x1A, 0x3A, 0x5C),
        "body_text":    RGBColor(0x1A, 0x1A, 0x1A),
        "accent":       RGBColor(0xE8, 0x8A, 0x00),   # amber
        "background":   RGBColor(0xF5, 0xF7, 0xFA),
    },
    "green_academic": {
        "header_bg":    RGBColor(0x1B, 0x4B, 0x36),   # forest green
        "header_text":  RGBColor(0xFF, 0xFF, 0xFF),
        "section_bg":   RGBColor(0xD8, 0xED, 0xE3),   # light green
        "section_header": RGBColor(0x1B, 0x4B, 0x36),
        "body_text":    RGBColor(0x1A, 0x1A, 0x1A),
        "accent":       RGBColor(0xC0, 0x39, 0x2B),   # red
        "background":   RGBColor(0xF4, 0xF9, 0xF6),
    },
    "crimson_grey": {
        "header_bg":    RGBColor(0x8B, 0x00, 0x00),   # crimson
        "header_text":  RGBColor(0xFF, 0xFF, 0xFF),
        "section_bg":   RGBColor(0xF0, 0xE8, 0xE8),   # blush
        "section_header": RGBColor(0x8B, 0x00, 0x00),
        "body_text":    RGBColor(0x1A, 0x1A, 0x1A),
        "accent":       RGBColor(0x2C, 0x3E, 0x50),   # slate
        "background":   RGBColor(0xFA, 0xF9, 0xF9),
    },
    "purple_modern": {
        "header_bg":    RGBColor(0x4A, 0x14, 0x8C),   # deep purple
        "header_text":  RGBColor(0xFF, 0xFF, 0xFF),
        "section_bg":   RGBColor(0xED, 0xE7, 0xF6),   # lavender
        "section_header": RGBColor(0x4A, 0x14, 0x8C),
        "body_text":    RGBColor(0x1A, 0x1A, 0x1A),
        "accent":       RGBColor(0xF5, 0x7C, 0x00),   # orange
        "background":   RGBColor(0xFA, 0xF8, 0xFF),
    },
    "monochrome": {
        "header_bg":    RGBColor(0x21, 0x21, 0x21),
        "header_text":  RGBColor(0xFF, 0xFF, 0xFF),
        "section_bg":   RGBColor(0xEE, 0xEE, 0xEE),
        "section_header": RGBColor(0x21, 0x21, 0x21),
        "body_text":    RGBColor(0x1A, 0x1A, 0x1A),
        "accent":       RGBColor(0x75, 0x75, 0x75),
        "background":   RGBColor(0xFF, 0xFF, 0xFF),
    },
}

DEFAULT_SCHEME = "classic_blue"
```

---

## Helper Functions

```python
# ── Drawing helpers ────────────────────────────────────────────────────────────

from pptx.oxml.ns import qn
from lxml import etree


def set_background_color(slide, color: RGBColor):
    """Fill the slide background with a solid color."""
    background = slide.background
    fill = background.fill
    fill.solid()
    fill.fore_color.rgb = color


def add_filled_rectangle(slide, left, top, width, height,
                          fill_color: RGBColor, line_color=None, line_width_pt=0):
    """Add a solid filled rectangle shape (used for section boxes and header)."""
    shape = slide.shapes.add_shape(
        1,  # MSO_SHAPE_TYPE.RECTANGLE
        left, top, width, height
    )
    shape.fill.solid()
    shape.fill.fore_color.rgb = fill_color

    if line_color:
        shape.line.color.rgb = line_color
        shape.line.width = Pt(line_width_pt)
    else:
        shape.line.fill.background()  # no border

    return shape


def add_textbox(slide, left, top, width, height, text: str,
                font_size: int, font_color: RGBColor,
                bold=False, italic=False, alignment=PP_ALIGN.LEFT,
                word_wrap=True, font_name="Calibri"):
    """Add a text box with specified formatting."""
    txBox = slide.shapes.add_textbox(left, top, width, height)
    tf = txBox.text_frame
    tf.word_wrap = word_wrap

    p = tf.paragraphs[0]
    p.alignment = alignment
    run = p.add_run()
    run.text = text

    font = run.font
    font.name = font_name
    font.size = Pt(font_size)
    font.color.rgb = font_color
    font.bold = bold
    font.italic = italic

    return txBox


def add_multiline_textbox(slide, left, top, width, height, lines: list,
                           font_size: int, font_color: RGBColor,
                           bold_first=False, font_name="Calibri",
                           bullet=False, line_spacing_pt=None):
    """
    Add a text box with multiple paragraphs (one per item in `lines`).
    If bullet=True, prepends 'β€’ ' to each line.
    """
    txBox = slide.shapes.add_textbox(left, top, width, height)
    tf = txBox.text_frame
    tf.word_wrap = True

    for i, line in enumerate(lines):
        if i == 0:
            p = tf.paragraphs[0]
        else:
            p = tf.add_paragraph()

        p.alignment = PP_ALIGN.LEFT
        run = p.add_run()
        prefix = "β€’ " if bullet else ""
        run.text = prefix + line

        font = run.font
        font.name = font_name
        font.size = Pt(font_size)
        font.color.rgb = font_color
        font.bold = (bold_first and i == 0)

        if line_spacing_pt:
            from pptx.util import Pt as pPt
            from pptx.oxml.ns import qn
            pPr = p._pPr
            if pPr is None:
                pPr = p._p.get_or_add_pPr()
            lnSpc = etree.SubElement(pPr, qn("a:lnSpc"))
            spcPts = etree.SubElement(lnSpc, qn("a:spcPts"))
            spcPts.set("val", str(int(line_spacing_pt * 100)))

    return txBox


def add_image(slide, image_path: str, left, top, width, height=None):
    """
    Insert an image at the specified position.
    If height is None, python-pptx preserves the aspect ratio.
    """
    if not os.path.exists(image_path):
        print(f"[WARNING] Image not found: {image_path} β€” skipping.")
        return None

    if height is None:
        pic = slide.shapes.add_picture(image_path, left, top, width=width)
    else:
        pic = slide.shapes.add_picture(image_path, left, top, width, height)

    return pic


def add_section_header(slide, left, top, width, height,
                        title: str, scheme: dict, font_size=24):
    """Draw a colored section header bar with white text."""
    add_filled_rectangle(slide, left, top, width, height,
                         fill_color=scheme["section_header"])
    add_textbox(slide, left + Cm(0.3), top, width - Cm(0.3), height,
                text=title,
                font_size=font_size,
                font_color=scheme["header_text"],
                bold=True,
                alignment=PP_ALIGN.LEFT)
```

---

## Poster Header Section

```python
def build_header(slide, scheme: dict,
                 title: str,
                 authors: str,
                 affiliations: str,
                 logo_left_path: str = None,
                 logo_right_path: str = None,
                 poster_width_cm: float = POSTER_WIDTH_CM):
    """
    Build the full-width header: logos on left/right, title/authors/affiliations centre.
    Header height = ~15% of poster height.
    """
    HEADER_HEIGHT = cm(12)
    LOGO_WIDTH    = cm(12)
    PADDING       = cm(1)

    # Background bar
    add_filled_rectangle(slide,
                         left=cm(0), top=cm(0),
                         width=cm(poster_width_cm), height=HEADER_HEIGHT,
                         fill_color=scheme["header_bg"])

    # Left logo
    if logo_left_path and os.path.exists(logo_left_path):
        add_image(slide, logo_left_path,
                  left=PADDING, top=cm(1),
                  width=LOGO_WIDTH, height=cm(10))

    # Right logo
    if logo_right_path and os.path.exists(logo_right_path):
        add_image(slide, logo_right_path,
                  left=cm(poster_width_cm) - LOGO_WIDTH - PADDING,
                  top=cm(1),
                  width=LOGO_WIDTH, height=cm(10))

    # Title β€” centered
    text_left  = LOGO_WIDTH + PADDING * 2
    text_width = cm(poster_width_cm) - (LOGO_WIDTH + PADDING) * 2

    add_textbox(slide,
                left=text_left, top=cm(1),
                width=text_width, height=cm(5),
                text=title,
                font_size=52,
                font_color=scheme["header_text"],
                bold=True,
                alignment=PP_ALIGN.CENTER)

    add_textbox(slide,
                left=text_left, top=cm(6),
                width=text_width, height=cm(2.5),
                text=authors,
                font_size=28,
                font_color=scheme["header_text"],
                bold=False,
                alignment=PP_ALIGN.CENTER)

    add_textbox(slide,
                left=text_left, top=cm(8.5),
                width=text_width, height=cm(2),
                text=affiliations,
                font_size=22,
                font_color=scheme["header_text"],
                italic=True,
                alignment=PP_ALIGN.CENTER)
```

---

## Section Building Blocks

```python
def build_text_section(slide, left, top, width, height,
                        section_title: str,
                        content_lines: list,
                        scheme: dict,
                        header_height_cm: float = 2.0,
                        font_size: int = 20,
                        bullet: bool = True):
    """
    Draw a complete section box: colored header + white body with text lines.
    """
    HDR = cm(header_height_cm)

    # Section background
    add_filled_rectangle(slide, left, top, width, height,
                         fill_color=scheme["section_bg"],
                         line_color=scheme["section_header"],
                         line_width_pt=1.5)

    # Section header bar
    add_section_header(slide, left, top, width, HDR,
                       title=section_title, scheme=scheme)

    # Body text
    add_multiline_textbox(slide,
                          left=left + cm(0.5),
                          top=top + HDR + cm(0.3),
                          width=width - cm(1),
                          height=height - HDR - cm(0.5),
                          lines=content_lines,
                          font_size=font_size,
                          font_color=scheme["body_text"],
                          bullet=bullet)


def build_figure_section(slide, left, top, width, height,
                          section_title: str,
                          image_path: str,
                          caption: str,
                          scheme: dict,
                          header_height_cm: float = 2.0,
                          caption_height_cm: float = 2.5):
    """
    Draw a section box containing a figure + caption below it.
    """
    HDR     = cm(header_height_cm)
    CAPTION = cm(caption_height_cm)
    PAD     = cm(0.4)

    # Background
    add_filled_rectangle(slide, left, top, width, height,
                         fill_color=scheme["section_bg"],
                         line_color=scheme["section_header"],
                         line_width_pt=1.5)

    # Header
    add_section_header(slide, left, top, width, HDR,
                       title=section_title, scheme=scheme)

    # Image area
    img_top    = top + HDR + PAD
    img_height = height - HDR - CAPTION - PAD * 2

    if image_path:
        add_image(slide, image_path,
                  left=left + PAD,
                  top=img_top,
                  width=width - PAD * 2,
                  height=img_height)
    else:
        # Placeholder grey box when no image provided
        add_filled_rectangle(slide,
                             left=left + PAD, top=img_top,
                             width=width - PAD * 2, height=img_height,
                             fill_color=RGBColor(0xCC, 0xCC, 0xCC))

    # Caption
    add_textbox(slide,
                left=left + PAD,
                top=top + HDR + PAD + img_height,
                width=width - PAD * 2,
                height=CAPTION,
                text=caption,
                font_size=18,
                font_color=scheme["body_text"],
                italic=True,
                alignment=PP_ALIGN.CENTER)
```

---

## Full Poster Assembly β€” Three-Column A0 Landscape

```python
def build_a0_landscape_poster(
    title: str,
    authors: str,
    affiliations: str,
    abstract_lines: list,
    intro_lines: list,
    methods_lines: list,
    results_lines: list,
    conclusions_lines: list,
    acknowledgements: str,
    references_lines: list,
    figure1_path: str = None,
    figure1_caption: str = "Figure 1.",
    figure2_path: str = None,
    figure2_caption: str = "Figure 2.",
    methods_figure_path: str = None,
    methods_figure_caption: str = "Workflow.",
    logo_left: str = None,
    logo_right: str = None,
    color_scheme: str = "classic_blue",
    output_path: str = "poster.pptx"
):
    """
    Build a complete three-column A0 landscape scientific poster.

    Layout (all measurements in cm from top-left origin):
      Header:  full width, 0–12 cm
      Column 1 (left):   0–38 cm wide,  12–82 cm tall
      Column 2 (middle): 40–78 cm wide, 12–82 cm tall
      Column 3 (right):  80–118 cm wide, 12–82 cm tall
      Footer:  full width, 82–84.1 cm
    """
    scheme = SCHEMES.get(color_scheme, SCHEMES[DEFAULT_SCHEME])
    prs, slide = create_poster(output_path)

    # Poster background
    set_background_color(slide, scheme["background"])

    # ── HEADER ──────────────────────────────────────────────────────────────
    build_header(slide, scheme,
                 title=title, authors=authors, affiliations=affiliations,
                 logo_left_path=logo_left, logo_right_path=logo_right)

    # ── LAYOUT CONSTANTS ────────────────────────────────────────────────────
    COL_TOP    = cm(12.5)
    COL_BOTTOM = cm(82)
    COL_HEIGHT = COL_BOTTOM - COL_TOP
    GAP        = cm(1.5)

    C1_LEFT  = cm(1)
    C1_WIDTH = cm(37)
    C2_LEFT  = C1_LEFT + C1_WIDTH + GAP
    C2_WIDTH = cm(37)
    C3_LEFT  = C2_LEFT + C2_WIDTH + GAP
    C3_WIDTH = cm(POSTER_WIDTH_CM) - C3_LEFT - cm(1)

    # ── COLUMN 1: Abstract + Introduction ───────────────────────────────────
    ABSTRACT_H = cm(22)
    build_text_section(slide,
                       left=C1_LEFT, top=COL_TOP,
                       width=C1_WIDTH, height=ABSTRACT_H,
                       section_title="Abstract",
                       content_lines=abstract_lines,
                       scheme=scheme, bullet=False, font_size=19)

    INTRO_H = COL_HEIGHT - ABSTRACT_H - GAP
    build_text_section(slide,
                       left=C1_LEFT, top=COL_TOP + ABSTRACT_H + GAP,
                       width=C1_WIDTH, height=INTRO_H,
                       section_title="Introduction & Background",
                       content_lines=intro_lines,
                       scheme=scheme, bullet=True, font_size=20)

    # ── COLUMN 2: Methods + Methods Figure ──────────────────────────────────
    METHODS_TEXT_H  = cm(28)
    METHODS_FIG_H   = COL_HEIGHT - METHODS_TEXT_H - GAP

    build_text_section(slide,
                       left=C2_LEFT, top=COL_TOP,
                       width=C2_WIDTH, height=METHODS_TEXT_H,
                       section_title="Methods",
                       content_lines=methods_lines,
                       scheme=scheme, bullet=True, font_size=20)

    build_figure_section(slide,
                          left=C2_LEFT,
                          top=COL_TOP + METHODS_TEXT_H + GAP,
                          width=C2_WIDTH, height=METHODS_FIG_H,
                          section_title="Workflow",
                          image_path=methods_figure_path,
                          caption=methods_figure_caption,
                          scheme=scheme)

    # ── COLUMN 3: Results (2 figures) + Conclusions ─────────────────────────
    FIG1_H        = cm(26)
    FIG2_H        = cm(22)
    CONCLUSIONS_H = COL_HEIGHT - FIG1_H - FIG2_H - GAP * 2

    build_figure_section(slide,
                          left=C3_LEFT, top=COL_TOP,
                          width=C3_WIDTH, height=FIG1_H,
                          section_title="Results",
                          image_path=figure1_path,
                          caption=figure1_caption,
                          scheme=scheme)

    build_figure_section(slide,
                          left=C3_LEFT, top=COL_TOP + FIG1_H + GAP,
                          width=C3_WIDTH, height=FIG2_H,
                          section_title="",
                          image_path=figure2_path,
                          caption=figure2_caption,
                          scheme=scheme)

    build_text_section(slide,
                       left=C3_LEFT,
                       top=COL_TOP + FIG1_H + FIG2_H + GAP * 2,
                       width=C3_WIDTH, height=CONCLUSIONS_H,
                       section_title="Conclusions",
                       content_lines=conclusions_lines,
                       scheme=scheme, bullet=True, font_size=20)

    # ── FOOTER: Acknowledgements + References ───────────────────────────────
    FOOTER_TOP = cm(82.5)
    FOOTER_H   = cm(POSTER_HEIGHT_CM) - FOOTER_TOP
    HALF_W     = (cm(POSTER_WIDTH_CM) - cm(2)) / 2

    build_text_section(slide,
                       left=cm(1), top=FOOTER_TOP,
                       width=HALF_W, height=FOOTER_H,
                       section_title="Acknowledgements",
                       content_lines=[acknowledgements],
                       scheme=scheme, bullet=False, font_size=16)

    build_text_section(slide,
                       left=cm(1) + HALF_W + GAP, top=FOOTER_TOP,
                       width=HALF_W - GAP, height=FOOTER_H,
                       section_title="References",
                       content_lines=references_lines,
                       scheme=scheme, bullet=False, font_size=15)

    # ── SAVE ────────────────────────────────────────────────────────────────
    prs.save(output_path)
    print(f"[OK] Poster saved: {output_path}")
    return output_path
```

---

## Example Usage

```python
if __name__ == "__main__":
    build_a0_landscape_poster(
        title="Deep Learning for Early Detection of Alzheimer's Disease\nUsing Multimodal Neuroimaging",
        authors="J. SmithΒΉ, A. PatelΒ², R. MΓΌllerΒΉ, L. ChenΒ³",
        affiliations="ΒΉDept. of Neuroscience, University of Example | Β²Brain Imaging Centre, City Hospital | Β³ML Lab, Tech Institute",

        abstract_lines=[
            "Alzheimer's disease (AD) affects over 55 million people worldwide. "
            "Early and accurate diagnosis remains a significant clinical challenge. "
            "We present a multimodal deep learning framework integrating structural MRI, "
            "FDG-PET, and cerebrospinal fluid biomarkers to improve early AD detection. "
            "Our model achieves 94.2% accuracy on the ADNI dataset, outperforming "
            "unimodal baselines by 8.3 percentage points. These results suggest "
            "multimodal fusion substantially improves pre-clinical AD diagnosis."
        ],

        intro_lines=[
            "Alzheimer's disease is the leading cause of dementia, with costs exceeding $300B/year in the US alone.",
            "Current diagnostic methods rely on late-stage symptom presentation, missing the critical early treatment window.",
            "Neuroimaging biomarkers (MRI atrophy, PET hypometabolism) show promise but are typically analysed in isolation.",
            "Deep learning enables automated feature extraction from high-dimensional neuroimaging data.",
            "We hypothesise that multimodal fusion will significantly outperform single-modality approaches for early AD classification.",
        ],

        methods_lines=[
            "Dataset: 1,200 subjects from ADNI (400 CN, 400 MCI, 400 AD).",
            "Modalities: T1-weighted MRI (3T), FDG-PET, CSF AΞ²42/tau ratios.",
            "Preprocessing: FreeSurfer cortical parcellation, SPM12 PET normalisation.",
            "Architecture: Three-stream CNN encoders with cross-modal attention fusion.",
            "Training: 5-fold cross-validation, AdamW optimiser, cosine LR schedule.",
            "Evaluation: Accuracy, AUC-ROC, sensitivity/specificity for CN vs. MCI vs. AD.",
        ],

        results_lines=[
            "94.2% overall accuracy (vs. 86.1% MRI-only baseline).",
            "AUC-ROC: 0.97 for AD vs. CN; 0.89 for MCI vs. CN.",
            "Cross-modal attention identified hippocampus, entorhinal cortex, and precuneus as most predictive.",
            "Model generalises across APOE-Ξ΅4 carrier subgroups (p > 0.05 for subgroup differences).",
        ],

        conclusions_lines=[
            "Multimodal deep learning significantly improves early AD detection over single-modality approaches.",
            "Cross-modal attention provides interpretable biomarker importance maps consistent with known AD pathology.",
            "The framework is scanner-agnostic and generalises across genetic risk subgroups.",
            "Future work: longitudinal modelling, external validation, prospective clinical study.",
        ],

        acknowledgements="Funded by NIH R01-AG012345 and the Alzheimer's Association Research Grant #AARG-22-000123. "
                         "Data provided by the Alzheimer's Disease Neuroimaging Initiative (ADNI). "
                         "Computing resources from the National Center for Supercomputing Applications.",

        references_lines=[
            "[1] Jack et al. (2018). NIA-AA Research Framework. Alzheimer's & Dementia, 14, 535–562.",
            "[2] Litjens et al. (2017). A survey on deep learning in medical image analysis. Med. Im. Analysis, 42, 60–88.",
            "[3] Ngiam et al. (2011). Multimodal deep learning. ICML 2011.",
            "[4] Zhang et al. (2022). Multimodal neuroimaging fusion for AD. NeuroImage, 249, 118907.",
            "[5] Petersen et al. (2014). ADNI: 10 years. Arch Neurol, 71, 806–813.",
        ],

        figure1_path="figures/roc_curves.png",
        figure1_caption="Figure 1. ROC curves for AD vs. CN (AUC = 0.97), MCI vs. CN (AUC = 0.89), and AD vs. MCI (AUC = 0.91).",
        figure2_path="figures/attention_map.png",
        figure2_caption="Figure 2. Cross-modal attention heatmaps overlaid on MRI showing hippocampal and entorhinal activation.",
        methods_figure_path="figures/architecture.png",
        methods_figure_caption="Figure 3. Three-stream CNN architecture with cross-modal attention fusion module.",

        logo_left="logos/university_logo.png",
        logo_right="logos/funder_logo.png",
        color_scheme="classic_blue",
        output_path="poster_ad_multimodal.pptx"
    )
```

---

## Portrait Poster (A0 / A1)

For portrait orientation, adjust dimensions and use a two-column layout:

```python
# A0 Portrait: 84.1 cm Γ— 118.9 cm
def create_portrait_poster(output_path="poster_portrait.pptx"):
    prs = Presentation()
    prs.slide_width  = Cm(84.1)
    prs.slide_height = Cm(118.9)
    slide_layout = prs.slide_layouts[6]
    slide = prs.slides.add_slide(slide_layout)
    return prs, slide

# A1 Landscape: 84.1 cm Γ— 59.4 cm
def create_a1_landscape(output_path="poster_a1.pptx"):
    prs = Presentation()
    prs.slide_width  = Cm(84.1)
    prs.slide_height = Cm(59.4)
    slide_layout = prs.slide_layouts[6]
    slide = prs.slides.add_slide(slide_layout)
    return prs, slide
```

---

## PDF Export

### Via LibreOffice (command line β€” cross-platform)

```bash
# Export .pptx to PDF using LibreOffice headless mode
libreoffice --headless --convert-to pdf poster_ad_multimodal.pptx --outdir ./

# Or specify output directory
libreoffice --headless --convert-to pdf:impress_pdf_Export poster.pptx --outdir output/
```

### Via Python subprocess

```python
import subprocess
import os

def export_to_pdf(pptx_path: str, output_dir: str = ".") -> str:
    """Convert .pptx to PDF using LibreOffice."""
    os.makedirs(output_dir, exist_ok=True)
    result = subprocess.run(
        ["libreoffice", "--headless", "--convert-to", "pdf",
         pptx_path, "--outdir", output_dir],
        capture_output=True, text=True
    )
    if result.returncode != 0:
        raise RuntimeError(f"LibreOffice conversion failed:\n{result.stderr}")

    pdf_name = os.path.splitext(os.path.basename(pptx_path))[0] + ".pdf"
    pdf_path = os.path.join(output_dir, pdf_name)
    print(f"[OK] PDF exported: {pdf_path}")
    return pdf_path
```

---

## Verification Checklist

Before delivering the poster:

- [ ] Title, authors, affiliations render correctly in header
- [ ] All sections have content (no empty boxes)
- [ ] All referenced image files exist and load without warnings
- [ ] Text is not clipped or overflowing section boxes (check at 100% zoom)
- [ ] Font sizes are legible at the target print size (β‰₯18pt for body, β‰₯24pt for section headers)
- [ ] Color scheme is consistent throughout
- [ ] .pptx opens correctly in both PowerPoint and LibreOffice
- [ ] PDF export renders without distortion

---

## Common Issues and Fixes

| Problem | Likely cause | Fix |
|---------|-------------|-----|
| Text overflows section box | Font size too large / too many lines | Reduce font size or split into two sections |
| Image not inserted | File path wrong or file missing | Check `os.path.exists(path)` before calling `add_image` |
| Wrong poster size | `slide_width`/`slide_height` set in inches, not EMUs | Use `Cm()` or `Inches()` wrappers, not raw integers |
| Logo appears stretched | Width and height both specified with wrong aspect ratio | Set only `width=` and let python-pptx auto-compute height |
| PDF looks different from .pptx | Font not installed on LibreOffice machine | Embed fonts or use system fonts (Calibri β†’ Liberation Sans) |
| Columns misaligned | Arithmetic error in column left/width calculations | Print all `left`, `top`, `width`, `height` values and verify sum |

---

## Integration with Scientific Schematics

For high-quality figures to embed in the poster, use the `scientific-schematics` skill before generating the poster:

```bash
# Generate workflow diagram for Methods section
python <path-to-scientific-schematics-skill>/scripts/generate_schematic.py \
  "Three-stream CNN architecture: MRI encoder, PET encoder, CSF encoder feeding into cross-modal attention fusion with final classification layer" \
  -o figures/architecture.png

# Generate results figure
python <path-to-scientific-schematics-skill>/scripts/generate_schematic.py \
  "ROC curves for three-class neuroimaging classification showing AD vs CN AUC 0.97, MCI vs CN AUC 0.89" \
  -o figures/roc_curves.png
```

Then pass the generated file paths as `figure1_path`, `figure2_path`, or `methods_figure_path` arguments to the poster builder.

---

## File Naming Conventions

```
posters/
└── YYYYMMDD_<short_title>/
    β”œβ”€β”€ poster.pptx          # Primary deliverable
    β”œβ”€β”€ poster.pdf           # PDF export
    β”œβ”€β”€ generate_poster.py   # Generation script (reproducible)
    └── figures/             # Embedded images
        β”œβ”€β”€ fig1_results.png
        β”œβ”€β”€ fig2_attention.png
        └── fig3_architecture.png
```

Signals

Avg rating⭐ 0.0
Reviews0
Favorites0

Information

Repository
K-Dense-AI/claude-scientific-writer
Author
K-Dense-AI
Last Sync
7/22/2026
Repo Updated
7/22/2026
Created
6/15/2026

Reviews (0)

No reviews yet. Be the first to review this skill!

Related Skills

cursorrules

CrewAI Development Rules

⭐ 43932Has guide

firecrawl-build-search

Integrate Firecrawl `/search` into product code and agent workflows. Use when an app needs discovery before extraction, when the feature starts with a query instead of a URL, or when the system should search the web and optionally hydrate result content.

⭐ 35092

firecrawl-build-onboarding

Get Firecrawl credentials and SDK setup into a project. Use when an application needs `FIRECRAWL_API_KEY`, when an agent should add Firecrawl to `.env`, when the user wants to authenticate Firecrawl for app code, or when choosing the first SDK and docs for a new Firecrawl integration. This skill includes its own browser auth flow, so it does not depend on the website onboarding skill.

⭐ 35092

firecrawl-build

Integrate Firecrawl into application code whenever a product, agent, or workflow needs web data inside the app β€” web search, live search results, page scraping, structured extraction, or browser interaction. Use when building any feature that needs data from the web in code, even if the user does not mention Firecrawl explicitly and only describes wanting web data, website content, search, scraping, or interaction in an application. Trigger for Firecrawl requests, "fire girl" shorthand, and generic app-level web-data needs that should map to `/scrape`, `/search`, or `/interact`. Do not use this skill for one-off terminal-only web tasks during the current session; use `firecrawl/cli` for those.

⭐ 35092

Related Guides