| name | poster-presentation |
| description | Create scientific conference posters as .pptx files using python-pptx. Handles A0/A1 layouts, section placement, figure insertion, and academic color schemes. Exports editable .pptx and PDF. |
| tools | Read, Write, Bash |
| version | 1.0.0 |
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:
pip install python-pptx Pillow
For PDF export from the command line:
brew install --cask libreoffice
sudo apt-get install libreoffice
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:
- Title — full paper/poster title
- Authors — list with superscript affiliation numbers
- Affiliations — institution names linked to authors
- Contact / corresponding author email
- Abstract — 150–250 words
- Introduction / Background — key context and motivation (3–5 bullet points or short paragraphs)
- Methods — concise description with optional workflow figure
- Results — key findings, data visualizations, tables
- Conclusions — 4–6 bullet points
- Acknowledgements — funding sources, collaborators
- References — 5–10 key references (abbreviated format)
- Figures — file paths to images/plots to embed
- Logo(s) — institutional/conference logo paths
- 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
"""
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
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)
slide_layout = prs.slide_layouts[6]
slide = prs.slides.add_slide(slide_layout)
return prs, slide
Academic Color Schemes
SCHEMES = {
"classic_blue": {
"header_bg": RGBColor(0x1A, 0x3A, 0x5C),
"header_text": RGBColor(0xFF, 0xFF, 0xFF),
"section_bg": RGBColor(0xD6, 0xE4, 0xF0),
"section_header": RGBColor(0x1A, 0x3A, 0x5C),
"body_text": RGBColor(0x1A, 0x1A, 0x1A),
"accent": RGBColor(0xE8, 0x8A, 0x00),
"background": RGBColor(0xF5, 0xF7, 0xFA),
},
"green_academic": {
"header_bg": RGBColor(0x1B, 0x4B, 0x36),
"header_text": RGBColor(0xFF, 0xFF, 0xFF),
"section_bg": RGBColor(0xD8, 0xED, 0xE3),
"section_header": RGBColor(0x1B, 0x4B, 0x36),
"body_text": RGBColor(0x1A, 0x1A, 0x1A),
"accent": RGBColor(0xC0, 0x39, 0x2B),
"background": RGBColor(0xF4, 0xF9, 0xF6),
},
"crimson_grey": {
"header_bg": RGBColor(0x8B, 0x00, 0x00),
"header_text": RGBColor(0xFF, 0xFF, 0xFF),
"section_bg": RGBColor(0xF0, 0xE8, 0xE8),
"section_header": RGBColor(0x8B, 0x00, 0x00),
"body_text": RGBColor(0x1A, 0x1A, 0x1A),
"accent": RGBColor(0x2C, 0x3E, 0x50),
"background": RGBColor(0xFA, 0xF9, 0xF9),
},
"purple_modern": {
"header_bg": RGBColor(0x4A, 0x14, 0x8C),
"header_text": RGBColor(0xFF, 0xFF, 0xFF),
"section_bg": RGBColor(0xED, 0xE7, 0xF6),
"section_header": RGBColor(0x4A, 0x14, 0x8C),
"body_text": RGBColor(0x1A, 0x1A, 0x1A),
"accent": RGBColor(0xF5, 0x7C, 0x00),
"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
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,
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()
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
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)
add_filled_rectangle(slide,
left=cm(0), top=cm(0),
width=cm(poster_width_cm), height=HEADER_HEIGHT,
fill_color=scheme["header_bg"])
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))
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))
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
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)
add_filled_rectangle(slide, left, top, width, height,
fill_color=scheme["section_bg"],
line_color=scheme["section_header"],
line_width_pt=1.5)
add_section_header(slide, left, top, width, HDR,
title=section_title, scheme=scheme)
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)
add_filled_rectangle(slide, left, top, width, height,
fill_color=scheme["section_bg"],
line_color=scheme["section_header"],
line_width_pt=1.5)
add_section_header(slide, left, top, width, HDR,
title=section_title, scheme=scheme)
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:
add_filled_rectangle(slide,
left=left + PAD, top=img_top,
width=width - PAD * 2, height=img_height,
fill_color=RGBColor(0xCC, 0xCC, 0xCC))
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
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)
set_background_color(slide, scheme["background"])
build_header(slide, scheme,
title=title, authors=authors, affiliations=affiliations,
logo_left_path=logo_left, logo_right_path=logo_right)
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)
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)
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)
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_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)
prs.save(output_path)
print(f"[OK] Poster saved: {output_path}")
return output_path
Example Usage
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:
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
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)
libreoffice --headless --convert-to pdf poster_ad_multimodal.pptx --outdir ./
libreoffice --headless --convert-to pdf:impress_pdf_Export poster.pptx --outdir output/
Via Python subprocess
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:
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:
python 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
python 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