| name | journal-reading |
| description | Convert a medical paper (PDF or folder with supplements) into a professional, academic PowerPoint presentation (PPTX) with extracted figures/tables, mirroring the paper's own structure, with clean medical aesthetics. |
| allowed-tools | Bash, Read, Write, Glob, Grep |
Journal Reading PPTX Conversion
Overview
When a user provides a medical paper and asks for a journal reading presentation (Journal Reading 簡報 / 晨會簡報), use this skill to generate a professional, academic python-pptx presentation. The input can be:
- A single PDF file — the main paper
- A folder containing the main paper PDF plus supplementary files (e.g., supplement PDFs, appendix tables, additional figures downloaded from the journal website)
The slide structure should follow the paper's own organization — not a fixed template — to faithfully represent the study's logic and highlight its academic rigor.
Prerequisites
- The python modules
python-pptx and pymupdf must be installed:
pip3 install python-pptx pymupdf
Workflow
0a. Ask for Presenter Information
Before starting any processing, ask the user for presenter information. This ensures the title slide and ending slide display the correct names. Present the question concisely — the user may skip it:
Presenter info: Who is presenting and who is the supervisor? (e.g., "R2 王大明 / VS 李教授") — press Enter to skip.
- If the user provides names → use them on the title slide and ending slide
- If the user skips (empty reply or says "skip" / "略過") → check memory for saved user profile; if none found, leave presenter info blank or use a generic placeholder ("Presenter / Supervisor")
- Only ask once at the beginning — do not re-ask during the workflow
0b. Identify Input & Create Output Folder
Detect input type
The user may provide:
- A single PDF file → treat it as the main paper
- A folder path → scan the folder for all relevant files
import os, re, glob
user_input = "..."
if os.path.isdir(user_input):
input_dir = user_input
all_pdfs = sorted(glob.glob(os.path.join(input_dir, "*.pdf")))
all_images = sorted(
glob.glob(os.path.join(input_dir, "*.png")) +
glob.glob(os.path.join(input_dir, "*.jpg")) +
glob.glob(os.path.join(input_dir, "*.jpeg")) +
glob.glob(os.path.join(input_dir, "*.tif")) +
glob.glob(os.path.join(input_dir, "*.tiff"))
)
main_pdf = None
supplement_pdfs = []
for pdf in all_pdfs:
basename = os.path.basename(pdf).lower()
if any(kw in basename for kw in ["suppl", "supplement", "appendix", "table_s", "figure_s"]):
supplement_pdfs.append(pdf)
elif main_pdf is None:
main_pdf = pdf
else:
if os.path.getsize(pdf) > os.path.getsize(main_pdf):
supplement_pdfs.append(main_pdf)
main_pdf = pdf
else:
supplement_pdfs.append(pdf)
print(f"Main paper: {main_pdf}")
()
()
:
main_pdf = user_input
input_dir = os.path.dirname(user_input)
supplement_pdfs = []
all_images = []
Create output folder
Create a dedicated output folder in the same directory as the input:
{ShortTitle}_journal_reading/
├── figures/ ← extracted figures & tables (from main + supplements)
└── presentation.pptx ← final presentation
Naming convention: derive {ShortTitle} from the paper title — use 3-5 key English words in snake_case, e.g.:
- "The Effect of Topical Tranexamic Acid on..." →
topical_TXA_rhinoplasty_journal_reading/
- "A Randomized Trial of Platelet-Rich Plasma..." →
PRP_randomized_trial_journal_reading/
paper_title = "..."
short = "_".join(paper_title.split()[:5]).replace("/","_")
short = re.sub(r'[^a-zA-Z0-9_\-]', '', short)
base_dir = input_dir if os.path.isdir(user_input) else os.path.dirname(main_pdf)
output_dir = os.path.join(base_dir, f"{short}_journal_reading")
figures_dir = os.path.join(output_dir, "figures")
os.makedirs(figures_dir, exist_ok=True)
All subsequent outputs must be saved into this output_dir.
1. Read All Source Files
Main paper
Use the Read tool with pages parameter to read the main PDF, or pdftotext for full extraction:
pdftotext "paper.pdf" /tmp/paper_text.txt
Supplement PDFs
Read each supplement PDF as well — these often contain important supplementary tables, figures, methods, and sensitivity analyses:
for pdf in supplement_pdfs:
pdftotext "$pdf" "/tmp/supplement_$(basename $pdf .pdf).txt"
Standalone images
Copy any standalone images (e.g., high-res figures downloaded from the journal website) directly into figures/:
import shutil
for img in all_images:
shutil.copy2(img, os.path.join(figures_dir, os.path.basename(img)))
2. Extract Figures & Tables from All PDFs
Apply the extraction process to both the main paper and all supplement PDFs. Supplement PDFs often contain high-resolution versions of figures, extended data tables, and flow diagrams.
Use a three-tier approach with PyMuPDF (fitz) for maximum quality. Tier 1 MUST be caption-aware (see warning below).
⚠️ CRITICAL — DO NOT use page.get_images() indices to name files.
page.get_images(full=True) returns images in xref order (PDF resource
dictionary order), NOT spatial / reading order. When a page has multiple
figures, naming embedded_p{N}_1, embedded_p{N}_2 produces SWAPPED labels.
Real failure: in the Kappenstein 2026 thyroid paper, page 5 returned FIG 3
(bottom) before FIG 2 (top), and the same happened on page 6 with FIG 4 / 5.
Always use the caption-aware helper below, which sorts by spatial bbox
position and matches each image to its "FIG. N" caption text block.
import fitz
import os
import sys
SKILL_SCRIPTS = "<absolute path to>/.claude/skills/journal-reading/scripts"
sys.path.insert(0, SKILL_SCRIPTS)
from extract_figures_by_caption import extract_figures_with_captions
pdf_path = "paper.pdf"
saved = extract_figures_with_captions(pdf_path, figures_dir)
doc = fitz.open(pdf_path)
PADDING = 8
for page_idx in range(len(doc)):
page = doc[page_idx]
blocks = page.get_text("dict")["blocks"]
img_blocks = [b for b in blocks if b["type"] == 1]
for i, block in enumerate(img_blocks):
bbox = block[]
()
scale =
mat = fitz.Matrix(scale, scale)
i, page (doc):
pix = page.get_pixmap(matrix=mat)
pix.save(os.path.join(figures_dir, ))
():
page = doc[page_idx]
page_rect = page.rect
x0 = (rect_tuple[] - padding, page_rect.x0)
y0 = (rect_tuple[] - padding, page_rect.y0)
x1 = (rect_tuple[] + padding, page_rect.x1)
y1 = (rect_tuple[] + padding, page_rect.y1)
clip = fitz.Rect(x0, y0, x1, y1)
pix = page.get_pixmap(matrix=fitz.Matrix(, ), clip=clip)
pix.save(os.path.join(figures_dir, filename))
doc.close()
Precise cropping workflow (CRITICAL)
Academic PDFs pack figures, tables, captions, footnotes, and body text tightly together. Guessing crop coordinates by eye leads to stray text bleeding into the crop (e.g., page headers, adjacent table footnotes, neighboring figure captions). You MUST follow this two-step process:
Step 1 — Block analysis (mandatory before ANY crop):
Run page.get_text("dict")["blocks"] on every page that contains a figure or table you need. Print each block's type (TXT=0, IMG=1) and bbox, plus a text preview for TXT blocks. This gives you the exact pixel boundaries of every element on the page.
for page_idx in pages_with_figures:
page = doc[page_idx]
blocks = page.get_text("dict")["blocks"]
for i, b in enumerate(blocks):
btype = "IMG" if b["type"] == 1 else "TXT"
bbox = [round(x, 1) for x in b["bbox"]]
if btype == "TXT":
text_preview = ""
for line in b.get("lines", []):
for span in line.get("spans", []):
text_preview += span["text"] + " "
text_preview = text_preview.strip()[:80]
print(f" Block {i:2d} [{btype}] bbox={bbox} \"{text_preview}\"")
else:
print(f" Block {i:2d} [{btype}] bbox={bbox}")
Step 2 — Derive crop coordinates from block boundaries:
- For a figure: use the IMG block's bbox as the top boundary, and its caption TXT block's bbox bottom as the lower boundary
- For a table: use the table title TXT block's bbox top as the upper boundary, and the last footnote TXT block's bbox bottom as the lower boundary
- Exclude adjacent elements: page headers (e.g., "Plastic and Reconstructive Surgery • March 2024"), body text paragraphs, other figures' captions, copyright lines
- Use small padding (4–6pt) — just enough for clean edges without capturing neighboring content
crop_save(page_idx, (145, 192, 440, 455), "fig1.png", padding=4)
Step 3 — Visual verification (mandatory):
After cropping, use the Read tool to view each cropped image and confirm:
- No stray text from adjacent elements (headers, body text, other tables/figures)
- The complete figure/table is captured including title, data, and footnotes
- If any crop is wrong, re-examine block coordinates and re-crop
Figure extraction decision guide
| Scenario | Method | Notes |
|---|
| Standalone photo/chart as raster image | Tier 1 (extract_image) | Best quality — native resolution |
| Need precise crop of a figure region | Tier 2 (block analysis → crop_save()) | MUST run block analysis first |
| Complex figure with caption, or table | Tier 2 (block analysis → crop_save()) | Use block boundaries, not guesses |
| Vector graphics (PDF-drawn charts) | Tier 2 block analysis + higher scale (3.5–4.0) | Won't appear in get_images() |
Key principles:
- NEVER guess crop coordinates — always derive them from
get_text("dict")["blocks"] bounding boxes
- Always use small padding (4–6pt) when cropping — large padding captures neighboring elements
- Tier 1 must be caption-aware — use
extract_figures_with_captions(), never get_images() index
- For tables: include title block through footnote blocks, but NOT adjacent body text or page headers
- For figures: include IMG block through caption block, but NOT adjacent tables or text
- Verify FIG-N mapping AND content: after Tier 1, read each
figN.{ext} with the Read tool and confirm the image content matches what FIG N is described as in the paper (e.g., fig2.jpeg must show whatever the paper's "FIG. 2." caption describes — not just a clean crop). The caption-matching helper is robust on standard journal layouts but can fail on multi-panel figures with sub-captions only ("a)", "b)" without "FIG N"); always cross-check.
- Verify ALL Tier-2/3 crops visually: read each cropped image to confirm no stray content before proceeding — if wrong, re-crop immediately
3. Analyze the Paper Structure
Read the full text carefully. Identify the paper's own sections (e.g., Introduction, Methods, Results, Discussion, Conclusion) and key elements:
- Title, authors, journal, year, DOI
- Study type (RCT, cohort, meta-analysis, case series, systematic review, etc.)
- Level of Evidence (LOE)
- PICO — Population, Intervention, Comparison, Outcome
- Key tables and figures — map extracted images to their original labels (Table 1, Figure 2, etc.)
- Statistical results — p-values, confidence intervals, effect sizes, NNT
- Limitations and strengths
- Clinical implications
3.5. Text Formatting Rules (CRITICAL)
These rules apply to ALL python-pptx text in the generated script. Violating them causes formatting bugs (black text, missing font sizes).
Never use p.text = "..."
Always use set_run() or p.add_run() to add text. The p.text = pattern creates a run but does not guarantee formatting on subsequent lines.
p.text = "Line 1\nLine 2\nLine 3"
run = p.runs[0]
run.font.size = Pt(22)
for i, line in enumerate(lines):
p = tf.paragraphs[0] if i == 0 else tf.add_paragraph()
run = p.add_run()
run.text = line
run.font.size = Pt(22)
run.font.color.rgb = DARK_GRAY
run.font.name = "Helvetica"
Never use \n in text strings
Each visual line must be a separate paragraph. Split multi-line content into a list before passing to any helper function.
Every text element must have explicit formatting
Every run must set: font.size, font.color.rgb, font.name. Never rely on defaults.
4. Design Slides Based on the Paper's Structure
Do NOT force a fixed slide count or fixed template. The number of slides should be determined by the content — use as many slides as needed to present the material clearly with comfortable spacing. Prefer more slides with less content each over fewer dense slides. A typical journal reading presentation may range from 15 to 25+ slides depending on the paper's complexity.
Layout density principles (critical):
- Max 4–5 bullet points per slide — each bullet should be one concise line
- Max 1 table + 1–2 figures per slide — if a slide has a table AND figures AND a highlight box, split it
- Two-column layouts: max 4–5 items per column
- Leave breathing room — generous padding, whitespace between elements; do not fill every pixel
- When in doubt, split — it is always better to add a slide than to cram content
- Font sizes must be readable from the back of a conference room — body text ≥ Pt(22), table cells ≥ Pt(16), titles ≥ Pt(32)
Required slides (always present):
- Title slide — Paper title, authors, journal, year, LOE badge, presenter info
- Outline slide (2nd slide) — Numbered table of contents listing all subsequent sections. This serves as a roadmap for the audience and must match the actual slide titles that follow.
- Background / Introduction — Clinical problem, knowledge gap, study rationale (1–2 slides)
- Study Objective & PICO — separate slide for clarity
- Study Design & Methods — split into multiple slides if needed (e.g., design + intervention on one, outcome assessment on another, grading scales/statistics on another)
- Results — one slide per major outcome; add a summary comparison slide if multiple outcomes exist
- Discussion (multiple slides) — This section should be detailed and thorough, with each sub-topic on its own slide: key findings, mechanism of action, comparison with literature, strengths & limitations, clinical implications, and future research directions. Never condense Discussion into fewer than 4 slides. When citing other studies in the Discussion, always include the author name, year, and key finding (e.g., "Ghavimi et al. (2017): IV TA reduced edema at 24 hrs"). These references come from the paper's own Discussion section — faithfully attribute claims to their cited sources rather than presenting them as standalone facts.
- Conclusions — key findings + clinical pearl
- Ending slide (last slide) — "Thank you — Questions?" with presenter info
Adapt to paper type:
| Paper Type | Structural Emphasis |
|---|
| RCT | CONSORT flow, intervention details, primary/secondary endpoints |
| Meta-analysis | PRISMA flow, forest plots, heterogeneity, subgroup analyses |
| Cohort / Case-control | Exposure definition, matching, confounders, adjusted estimates |
| Systematic review | Search strategy, inclusion criteria, quality assessment |
| Case series / report | Clinical presentation, timeline, management, outcome |
| Diagnostic study | Reference standard, sensitivity/specificity, ROC, STARD |
Slide Layout Patterns (16:9, coordinates in Inches)
Use these 5 patterns consistently. Reference them by name in code comments.
| Pattern | Layout | Coordinates | When to use |
|---|
| A: Figure + Analysis | Image left, bullets right | Image: (0.6, 1.4, w=6.0), Bullets: (7.0, 1.4, w=5.7, h=5.0) | Single figure with interpretation |
| B: Figure + Analysis (reversed) | Bullets left, image right | Bullets: (0.6, 1.4, w=5.7, h=5.0), Image: (6.8, 1.4, w=6.0) | Alternating visual flow |
| C: Table + Key Takeaway | Table top, highlight box bottom | Table: (0.6, 1.4, w=12.0, h=3.5), Box: (0.6, 5.2, w=12.0) | Results table with headline finding |
| D: Side-by-Side Comparison | Two images side by side, note below | Img1: (0.6, 1.4, w=5.8), Img2: (6.8, 1.4, w=5.8), Caption: (0.6, 6.0) | Comparing groups, before/after |
| E: Pure Content | Full-width bullets | Bullets: (0.8, 1.4, w=11.7, h=5.5), max 5 items | Intro, methods, discussion |
Emphasis Box Policy
Two types of emphasis boxes are available — use the appropriate one based on importance:
| Box Type | Function | Background | Text Color | When to Use |
|---|
add_highlight_box() | Supporting emphasis | Warm yellow (HIGHLIGHT_BG) + left gold accent bar | Dark text | Exclusion criteria, study rationale, secondary notes |
add_key_point() | Primary emphasis | Deep blue (KEY_POINT_BG) | White text, gold bold prefix | KEY FINDING, CLINICAL PEARL, most important takeaway |
- Max 1 emphasis box per slide — never stack multiple boxes
add_key_point() is for the single most important finding on a slide (e.g., significant result, clinical pearl)
add_highlight_box() is for supporting context (e.g., exclusion criteria, rationale)
- Position: typically at the bottom of the slide (Pattern C) or below bullets
Image-Text Pairing Rule
- Every figure/table MUST appear on the SAME slide as its interpretation text
- Use Pattern A or B to pair an image with analysis bullets
- NEVER isolate a figure on its own slide without interpretation
- NEVER put interpretation on a separate slide from its figure
Embedding figures in slides:
- Use
slide.shapes.add_picture() to insert extracted figures/tables into relevant slides
- Place figures alongside bullet-point summaries for context
- Maintain original figure/table labels as captions
- Size figures appropriately — typically
Inches(5) width for full-width, Inches(3.5) for side-by-side
from pptx.util import Inches
img_path = os.path.join(figures_dir, "figure1.png")
slide.shapes.add_picture(img_path, Inches(1), Inches(2), width=Inches(5))
Slide numbers (mandatory):
Every slide must display "X / N" in the bottom-right corner. Call add_slide_numbers(prs) from the helper library as the final step before prs.save(). This automatically adds numbers to all slides.
Academic quality principles:
- Faithfully represent the paper — preserve the authors' logic and data hierarchy
- Show raw data — include exact numbers, p-values, CIs; do not over-simplify
- Use proper statistical reporting — e.g., "OR 2.3 (95% CI 1.4–3.8, p=0.001)"
- Cite figures/tables by original labels — "Table 2", "Figure 3A"
- Include critical appraisal — bias assessment, study limitations, generalizability
- Highlight significant findings — use red/bold for significant p-values
Citation and attribution in Discussion slides:
- When the Discussion references other studies (comparison with literature, mechanism explanations, supporting evidence), always attribute the claim to its source with author name and year
- Format: "Author et al. (Year):" followed by key finding and study detail (e.g., sample size, route, outcome)
- Clearly distinguish between the current study's own findings vs claims from cited literature
- If the paper's Discussion explains a mechanism or makes an interpretive claim, note that it comes from the paper's own discussion (e.g., "The authors suggest..." or present it as the paper's interpretation)
- Do NOT present cited literature findings as if they are the current study's own results
- Example good format:
**Ghavimi et al. (2017):** IV TA in rhinoplasty (n=60) — reduced edema & ecchymosis at 24 hrs. *BUT systemic route*
- Example bad format:
IV TA reduces edema and ecchymosis at 24 hours (no attribution, unclear whose finding this is)
Slide layout and readability:
- Keep bullet points concise — max 4–5 per slide, one line each; split if more content is needed
- Font sizes for projection: body text ≥ Pt(22), titles ≥ Pt(32), table cells ≥ Pt(16), captions ≥ Pt(14)
- Spacious layout — do not pack slides tight; leave ≥ Inches(0.5) margins on all sides
- Figures: use
Inches(5–6) width for full-width; Inches(3–4) for side-by-side; always leave room for caption
- Tables: limit to 4–5 data rows per slide; split large tables across multiple slides if needed
- Widescreen format: use
prs.slide_width = Inches(13.333) and prs.slide_height = Inches(7.5) for 16:9 ratio
5. Generate the PPTX
Write a self-contained Python script to /tmp/create_presentation.py that inlines all helper functions from scripts/generate_aesthetic_pptx.py. Do NOT import from the helper file path — copy the function definitions directly into the script so it runs standalone.
Script structure:
- Inline all helpers at the top:
set_run(), create_presentation(), add_title_slide(), add_content_slide(), add_section_num(), add_outline_slide(), add_ending_slide(), add_bullets(), add_highlight_box(), add_key_point(), add_styled_table(), add_image(), add_caption(), add_slide_numbers(), _set_slide_bg(), _add_shape_with_fill(), _add_card_bg(), and all constants (DEEP_BLUE, MEDIUM_BLUE, DARK_TEXT, ACCENT_RED, SUCCESS_GREEN, WHITE, MUTED_GRAY, LIGHT_BG, CARD_BORDER, HIGHLIGHT_BG, KEY_POINT_BG, TABLE_ALT_ROW, SECTION_NUM_COLOR, font sizes, slide dimensions).
- Build slides using the layout patterns by name in comments (e.g.,
# Pattern A: Figure + Analysis).
- Call
add_slide_numbers(prs) as the final step before prs.save().
Key rules for the generated script:
- Use
add_content_slide(prs, title) for every content slide (creates light-bg slide with title bar + accent line)
- Use
add_section_num(slide, "01 — Methods") to add section number labels below title bar
- Use
add_bullets() for bullet lists — supports plain strings, ("Bold:", "rest") tuples, {"red": "p=0.001"} dicts, and {"green": "positive finding"} dicts
- Bold prefixes in tuples render in
DEEP_BLUE for high contrast; body text uses DARK_TEXT (#1E293B)
- Use
add_key_point() for KEY FINDING or CLINICAL PEARL — deep blue box with white/gold text
- Use
add_highlight_box() for supporting context — warm yellow box with gold accent bar
- Use
add_styled_table() with {"red": val} or {"green": val} dicts for colored cells
- Use
add_image() with existence check — always pair with analysis on the same slide
- Use
_add_card_bg() to create card-like backgrounds with left accent borders when grouping content visually
- Reference layout patterns A–E in comments for every slide
output_path = os.path.join(output_dir, "presentation.pptx")
add_slide_numbers(prs)
prs.save(output_path)
Execute the script:
python3 /tmp/create_presentation.py
6. Deliver
- All output files are saved inside the dedicated output folder:
{ShortTitle}_journal_reading/
├── figures/ ← extracted figures & tables
└── presentation.pptx ← final presentation
- Notify the user:
- The output folder path
- The number of slides generated and their section breakdown
- The number of figures/tables extracted and embedded
- That the file is fully editable in PowerPoint/Keynote
Content Guidelines
- Default language: English — use standard medical/academic terminology
- If the user requests Chinese or bilingual, switch accordingly
- Preserve the paper's own terminology and abbreviations
- Always include LOE and study design on the title slide
- Tables should use the styled format (deep-blue header, clean rows)
- Significant p-values: red bold text
- Non-significant results: still include them — academic honesty matters
- End with clinical relevance — what should the audience take away?
When to Use
This skill applies when a user:
- Provides a medical paper in PDF format
- Requests a "journal reading" / "Journal Reading 簡報" / "晨會簡報"
- Wants a PowerPoint (.pptx) presentation for academic presentation
- Mentions critical appraisal or evidence-based review