Explain me 10 common heart disease with Ecg and ecg inages of those diseases
ECG electrocardiogram myocardial infarction STEMI
atrial fibrillation ECG ventricular fibrillation arrhythmia
heart block complete AV block third degree ECG
ventricular tachycardia ECG wide QRS
left bundle branch block LBBB ECG
Wolff-Parkinson-White syndrome delta wave ECG pre-excitation
hypertrophic cardiomyopathy ECG left ventricular hypertrophy
long QT syndrome ECG torsades de pointes
pericarditis ECG saddle shaped ST elevation
right bundle branch block RBBB ECG RSR pattern
| Territory | Leads with ST Elevation | Culprit Artery |
|---|---|---|
| Anterior | V1βV4 | LAD |
| Inferior | II, III, aVF | RCA or LCx |
| Lateral | I, aVL, V5βV6 | LCx or LAD |
| Posterior | ST depression V1βV3 | RCA or LCx |















| # | Disease | Key ECG Finding |
|---|---|---|
| 1 | STEMI | Convex ST elevation + Q waves in territory |
| 2 | Atrial Fibrillation | No P waves, irregularly irregular |
| 3 | Complete AV Block | AV dissociation, escape rhythm |
| 4 | Ventricular Tachycardia | Wide QRS tachycardia, AV dissociation |
| 5 | LBBB | Broad QRS, dominant R in lateral leads |
| 6 | RBBB | RSR' in V1, slurred S in lateral leads |
| 7 | HCM | LVH voltage + deep T inversions |
| 8 | Long QT Syndrome | QTc >500 ms β Torsades de Pointes |
| 9 | Acute Pericarditis | Diffuse concave ST elevation + PR depression |
| 10 | WPW Syndrome | Short PR + delta wave + wide QRS |
Clinical Pearl: Always assess the ECG systematically β rate, rhythm, axis, intervals (PR, QRS, QTc), then ST-T changes. Pattern recognition is built on understanding the underlying electrophysiology of each condition.
How i get pdf of this
ls /home/daytona/workspace/ 2>/dev/null || echo "workspace is empty or doesn't eβ¦
mkdir -p /home/daytona/workspace/ecg-guide && echo "done"
~/ecg-guide/generate_ecg_pdf.py
#!/usr/bin/env python3
"""
Generate a professional PDF for: 10 Common Heart Diseases with ECG Findings
Uses reportlab for layout and urllib to download ECG images.
"""
import urllib.request
import os
from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.units import cm, mm
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.enums import TA_LEFT, TA_CENTER, TA_JUSTIFY
from reportlab.platypus import (
SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
Image, HRFlowable, PageBreak, KeepTogether
)
from reportlab.platypus.flowables import BalancedColumns
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
from io import BytesIO
OUTPUT = "/home/daytona/workspace/ecg-guide/10_Heart_Diseases_ECG_Guide.pdf"
IMG_DIR = "/home/daytona/workspace/ecg-guide/images"
os.makedirs(IMG_DIR, exist_ok=True)
# ββ ECG image URLs ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
IMAGES = {
"stemi_anterior": "https://cdn.orris.care/cdss_images/pmc_clinical_VQA_646aa0cfb0d976b56d82780dedb4cd025e7f67b7aa7d1163a2f05a58a08c514e.jpg",
"stemi_inferior": "https://cdn.orris.care/cdss_images/pmc_clinical_VQA_4ff7e7260a139d9714f42c31b67dc93d733282c66f59a068801ebd1d27bf5755.jpg",
"af_1": "https://cdn.orris.care/cdss_images/pmc_clinical_VQA_f3fac4aa2cfb9c0974c55a298765a969fb7578a83f9893789ff6a6e684381fd4.jpg",
"af_2": "https://cdn.orris.care/cdss_images/pmc_clinical_VQA_f421022f16424f2300a3805fed62a8eb70dcacd4e53d5924eef892432da41c11.jpg",
"av_block_1": "https://cdn.orris.care/cdss_images/pmc_clinical_VQA_8406f3f9d6a209bfd199f5a59f790f58cd2a726f5674a8e7fbed222b0a72b837.jpg",
"av_block_2": "https://cdn.orris.care/cdss_images/pmc_clinical_VQA_e815dc24a460bb845ac47baf936d46861eadc98131a930e5ce2dea5437cf2f45.jpg",
"vt_1": "https://cdn.orris.care/cdss_images/pmc_clinical_VQA_d15a92de4e89b46f273b22d254d1e30fcd6a190c6d581b193e6dd338338c6bb6.jpg",
"lbbb_1": "https://cdn.orris.care/cdss_images/pmc_clinical_VQA_ea8a2b2d91edc6e5739b3da41761971059234d08e77f2192005d3e07503597d5.jpg",
"rbbb_1": "https://cdn.orris.care/cdss_images/pmc_clinical_VQA_74308f325014c3be7af24c0cbb0aa2a6fe675afae1d1b0e5949c8797ae0beefa.jpg",
"hcm_1": "https://cdn.orris.care/cdss_images/pmc_clinical_VQA_2910b31808487d7cd1b8678e2a6942b2ca7518030d03f48787ac4b092bd7e92e.jpg",
"hcm_apical": "https://cdn.orris.care/cdss_images/pmc_clinical_VQA_f9136e8a77fe104cfbe3b8d6deb3a988a0925e95c5f6acba5afb957453257c47.jpg",
"lqt_1": "https://cdn.orris.care/cdss_images/HARRISON_1763034143291_d1a2f9c1-7f66-4dec-a3f8-19b81f2ba927.png",
"lqt_tdp": "https://cdn.orris.care/cdss_images/pmc_clinical_VQA_63220b51cce1f40d71e2eb97047a3e0d58ad25d9d9a9b1777a668ed3417326a2.jpg",
"pericarditis": "https://cdn.orris.care/cdss_images/pmc_clinical_VQA_cd4008531c1b1a2a99ab6d9b4282dcdcedfcf77c302b6ecbb7f7e01c5c985cf9.jpg",
"wpw_1": "https://cdn.orris.care/cdss_images/pmc_clinical_VQA_53c53d9ed7100de15d6d512e52e2a269252a79c85c12a60e54a4ce2fc4d574ec.jpg",
}
def download_image(key, url):
path = os.path.join(IMG_DIR, f"{key}.jpg")
if not os.path.exists(path):
try:
req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0"})
with urllib.request.urlopen(req, timeout=15) as r:
data = r.read()
with open(path, "wb") as f:
f.write(data)
print(f" β {key}")
except Exception as e:
print(f" β {key}: {e}")
return None
return path
def get_img(key, width=14*cm, height=None):
path = os.path.join(IMG_DIR, f"{key}.jpg")
if not os.path.exists(path):
return None
try:
img = Image(path)
aspect = img.imageHeight / float(img.imageWidth)
img.drawWidth = width
img.drawHeight = height if height else width * aspect
# cap height
if img.drawHeight > 7*cm:
img.drawHeight = 7*cm
img.drawWidth = img.drawHeight / aspect
return img
except:
return None
# ββ Download all images βββββββββββββββββββββββββββββββββββββββββββββββββββββ
print("Downloading ECG images...")
for k, u in IMAGES.items():
download_image(k, u)
# ββ Styles ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
styles = getSampleStyleSheet()
BRAND_BLUE = colors.HexColor("#1a3a5c")
ACCENT_BLUE = colors.HexColor("#2563eb")
LIGHT_BLUE = colors.HexColor("#e8f0fe")
ACCENT_RED = colors.HexColor("#dc2626")
LIGHT_GRAY = colors.HexColor("#f8fafc")
MID_GRAY = colors.HexColor("#64748b")
GREEN = colors.HexColor("#16a34a")
ORANGE = colors.HexColor("#ea580c")
title_style = ParagraphStyle("Title",
fontSize=26, fontName="Helvetica-Bold", textColor=BRAND_BLUE,
alignment=TA_CENTER, spaceAfter=4)
subtitle_style = ParagraphStyle("Subtitle",
fontSize=13, fontName="Helvetica", textColor=MID_GRAY,
alignment=TA_CENTER, spaceAfter=2)
disease_title_style = ParagraphStyle("DiseaseTitle",
fontSize=16, fontName="Helvetica-Bold", textColor=BRAND_BLUE,
spaceBefore=8, spaceAfter=4)
section_style = ParagraphStyle("Section",
fontSize=11, fontName="Helvetica-Bold", textColor=ACCENT_BLUE,
spaceBefore=6, spaceAfter=3)
body_style = ParagraphStyle("Body",
fontSize=10, fontName="Helvetica", textColor=colors.HexColor("#1e293b"),
leading=15, spaceAfter=3, alignment=TA_JUSTIFY)
bullet_style = ParagraphStyle("Bullet",
fontSize=10, fontName="Helvetica", textColor=colors.HexColor("#1e293b"),
leading=14, spaceAfter=2, leftIndent=14, bulletIndent=4)
caption_style = ParagraphStyle("Caption",
fontSize=8.5, fontName="Helvetica-Oblique", textColor=MID_GRAY,
alignment=TA_CENTER, spaceAfter=4)
toc_style = ParagraphStyle("TOC",
fontSize=11, fontName="Helvetica", textColor=BRAND_BLUE,
leading=18, spaceAfter=2)
def disease_number_badge(n):
"""Return a small colored badge table."""
data = [[Paragraph(f"<b>{n}</b>", ParagraphStyle("N", fontSize=13,
fontName="Helvetica-Bold", textColor=colors.white, alignment=TA_CENTER))]]
t = Table(data, colWidths=[0.7*cm], rowHeights=[0.7*cm])
t.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), ACCENT_BLUE),
("ROUNDEDCORNERS", [4]),
("VALIGN", (0,0), (-1,-1), "MIDDLE"),
("ALIGN", (0,0), (-1,-1), "CENTER"),
]))
return t
def section_header_table(number, title, subtitle=""):
"""Styled header row for each disease section."""
badge = disease_number_badge(number)
h = Paragraph(f"<b>{title}</b>", ParagraphStyle("H", fontSize=16,
fontName="Helvetica-Bold", textColor=BRAND_BLUE, leading=19))
sub = Paragraph(subtitle, ParagraphStyle("Sub", fontSize=10,
fontName="Helvetica-Oblique", textColor=MID_GRAY))
inner = Table([[h], [sub]], colWidths=[13*cm])
inner.setStyle(TableStyle([("LEFTPADDING",(0,0),(-1,-1),0),
("BOTTOMPADDING",(0,0),(-1,-1),0),
("TOPPADDING",(0,0),(-1,-1),0)]))
t = Table([[badge, inner]], colWidths=[1.1*cm, 13*cm])
t.setStyle(TableStyle([
("VALIGN",(0,0),(-1,-1),"MIDDLE"),
("LEFTPADDING",(0,0),(-1,-1),4),
("BACKGROUND",(0,0),(-1,-1),LIGHT_BLUE),
("BOX",(0,0),(-1,-1),1,colors.HexColor("#bfdbfe")),
("TOPPADDING",(0,0),(-1,-1),6),
("BOTTOMPADDING",(0,0),(-1,-1),6),
]))
return t
def ecg_box(key, caption, width=14*cm):
img = get_img(key, width=width)
items = []
if img:
items.append(img)
items.append(Paragraph(f"<i>{caption}</i>", caption_style))
box = Table([[items[0] if img else Paragraph("[Image unavailable]", caption_style)],
[items[-1]]], colWidths=[width])
box.setStyle(TableStyle([
("ALIGN",(0,0),(-1,-1),"CENTER"),
("BACKGROUND",(0,0),(0,0),colors.HexColor("#f1f5f9")),
("BOX",(0,0),(-1,-1),0.5,colors.HexColor("#cbd5e1")),
("TOPPADDING",(0,0),(-1,-1),4),
("BOTTOMPADDING",(0,0),(-1,-1),4),
]))
return box
def two_ecg_row(key1, cap1, key2, cap2):
"""Side-by-side ECG images."""
w = 6.8*cm
img1 = get_img(key1, width=w)
img2 = get_img(key2, width=w)
def cell(img, cap):
items = []
if img: items.append(img)
else: items.append(Paragraph("[Image unavailable]", caption_style))
items.append(Paragraph(f"<i>{cap}</i>", caption_style))
t = Table([[items[0]],[items[1]]], colWidths=[w])
t.setStyle(TableStyle([
("ALIGN",(0,0),(-1,-1),"CENTER"),
("BACKGROUND",(0,0),(0,0),colors.HexColor("#f1f5f9")),
("BOX",(0,0),(-1,-1),0.5,colors.HexColor("#cbd5e1")),
("TOPPADDING",(0,0),(-1,-1),3),
("BOTTOMPADDING",(0,0),(-1,-1),3),
]))
return t
row = Table([[cell(img1,cap1), cell(img2,cap2)]], colWidths=[w+4, w+4])
row.setStyle(TableStyle([("ALIGN",(0,0),(-1,-1),"CENTER"),
("VALIGN",(0,0),(-1,-1),"TOP"),
("LEFTPADDING",(0,0),(-1,-1),2),
("RIGHTPADDING",(0,0),(-1,-1),2)]))
return row
def bullets(items):
return [Paragraph(f"β’ {t}", bullet_style) for t in items]
def build_pdf():
doc = SimpleDocTemplate(OUTPUT, pagesize=A4,
leftMargin=1.8*cm, rightMargin=1.8*cm,
topMargin=2*cm, bottomMargin=2*cm,
title="10 Common Heart Diseases β ECG Guide",
author="Orris Medical AI")
story = []
# ββ COVER PAGE ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
story.append(Spacer(1, 1.5*cm))
# Top color bar
bar = Table([[""]],colWidths=[17*cm], rowHeights=[0.5*cm])
bar.setStyle(TableStyle([("BACKGROUND",(0,0),(-1,-1),ACCENT_BLUE)]))
story.append(bar)
story.append(Spacer(1, 0.6*cm))
story.append(Paragraph("10 Common Heart Diseases", title_style))
story.append(Paragraph("ECG Findings & Diagnostic Images", ParagraphStyle(
"Sub2", fontSize=16, fontName="Helvetica", textColor=ACCENT_BLUE,
alignment=TA_CENTER, spaceAfter=8)))
story.append(Spacer(1, 0.3*cm))
story.append(HRFlowable(width="100%", thickness=1, color=colors.HexColor("#bfdbfe")))
story.append(Spacer(1, 0.3*cm))
story.append(Paragraph("A comprehensive clinical reference with real ECG tracings",
subtitle_style))
story.append(Paragraph("Generated by Orris Medical AI Β· May 2026",
ParagraphStyle("Date", fontSize=9, fontName="Helvetica",
textColor=MID_GRAY, alignment=TA_CENTER)))
story.append(Spacer(1, 0.8*cm))
# Cover table of contents
toc_data = [
[Paragraph("<b>#</b>", ParagraphStyle("TH", fontSize=10, fontName="Helvetica-Bold",
textColor=colors.white, alignment=TA_CENTER)),
Paragraph("<b>Disease</b>", ParagraphStyle("TH", fontSize=10, fontName="Helvetica-Bold",
textColor=colors.white)),
Paragraph("<b>Key ECG Finding</b>", ParagraphStyle("TH", fontSize=10, fontName="Helvetica-Bold",
textColor=colors.white))],
["1", "ST-Elevation MI (STEMI)", "Convex ST elevation + Q waves"],
["2", "Atrial Fibrillation", "No P waves Β· Irregularly irregular"],
["3", "Complete AV Block (3Β°)", "AV dissociation Β· Escape rhythm"],
["4", "Ventricular Tachycardia", "Wide QRS tachycardia Β· AV dissociation"],
["5", "Left Bundle Branch Block", "Broad QRS Β· Dominant R in lateral leads"],
["6", "Right Bundle Branch Block", "RSR' in V1 Β· Slurred S in lateral leads"],
["7", "Hypertrophic Cardiomyopathy", "LVH voltage Β· Deep T-wave inversions"],
["8", "Long QT Syndrome", "QTc >500 ms β Torsades de Pointes"],
["9", "Acute Pericarditis", "Diffuse concave ST elevation Β· PR depression"],
["10","Wolff-Parkinson-White (WPW)", "Short PR Β· Delta wave Β· Wide QRS"],
]
row_colors = [
("BACKGROUND", (0, 0), (-1, 0), BRAND_BLUE),
]
for i in range(1, len(toc_data)):
bg = LIGHT_BLUE if i % 2 == 0 else colors.white
row_colors.append(("BACKGROUND", (0, i), (-1, i), bg))
toc_table = Table(toc_data, colWidths=[0.8*cm, 6*cm, 8.5*cm])
toc_table.setStyle(TableStyle([
("FONTNAME", (0, 1), (-1, -1), "Helvetica"),
("FONTSIZE", (0, 1), (-1, -1), 10),
("TEXTCOLOR", (0, 1), (-1, -1), BRAND_BLUE),
("ALIGN", (0, 0), (0, -1), "CENTER"),
("VALIGN", (0, 0), (-1, -1), "MIDDLE"),
("GRID", (0, 0), (-1, -1), 0.5, colors.HexColor("#e2e8f0")),
("TOPPADDING", (0, 0), (-1, -1), 5),
("BOTTOMPADDING", (0, 0), (-1, -1), 5),
("LEFTPADDING", (0, 0), (-1, -1), 6),
] + row_colors))
story.append(toc_table)
story.append(Spacer(1, 0.5*cm))
bot_bar = Table([[""]],colWidths=[17*cm], rowHeights=[0.3*cm])
bot_bar.setStyle(TableStyle([("BACKGROUND",(0,0),(-1,-1),ACCENT_BLUE)]))
story.append(bot_bar)
story.append(PageBreak())
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# DISEASE 1 β STEMI
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
story.append(section_header_table(1, "ST-Elevation Myocardial Infarction (STEMI)",
"Complete coronary occlusion causing transmural myocardial necrosis"))
story.append(Spacer(1, 0.3*cm))
story.append(Paragraph("Overview", section_style))
story.append(Paragraph(
"STEMI results from acute, complete occlusion of a coronary artery, leading to full-thickness "
"(transmural) ischemia and necrosis. It is a life-threatening emergency requiring immediate "
"reperfusion β primary percutaneous coronary intervention (PCI) within 90 minutes is the gold "
"standard. The ECG is the cornerstone of rapid diagnosis.", body_style))
story.append(Paragraph("ECG Features", section_style))
story += bullets([
"ST-segment elevation β₯1 mm in β₯2 contiguous limb leads, or β₯2 mm in precordial leads",
"Convex (tombstone) ST morphology β dome-shaped, high-risk marker",
"Reciprocal ST depression in mirror-image leads",
"Pathological Q waves develop within hours β indicate myocardial necrosis",
"Hyperacute T waves may precede ST elevation (earliest sign)",
"T-wave inversion follows in the evolutionary phase",
])
story.append(Spacer(1, 0.2*cm))
# Territory table
terr_data = [
[Paragraph("<b>Territory</b>", ParagraphStyle("TH", fontSize=9.5, fontName="Helvetica-Bold", textColor=colors.white)),
Paragraph("<b>Leads with ST Elevation</b>", ParagraphStyle("TH", fontSize=9.5, fontName="Helvetica-Bold", textColor=colors.white)),
Paragraph("<b>Culprit Artery</b>", ParagraphStyle("TH", fontSize=9.5, fontName="Helvetica-Bold", textColor=colors.white))],
["Anterior", "V1 β V4", "LAD"],
["Inferior", "II, III, aVF", "RCA or LCx"],
["Lateral", "I, aVL, V5 β V6", "LCx or LAD diagonal"],
["Posterior", "ST depression V1βV3 (mirror)", "RCA or LCx"],
]
terr_t = Table(terr_data, colWidths=[3.5*cm, 6*cm, 6*cm])
terr_t.setStyle(TableStyle([
("BACKGROUND",(0,0),(-1,0), BRAND_BLUE),
("FONTNAME",(0,1),(-1,-1),"Helvetica"), ("FONTSIZE",(0,0),(-1,-1),9.5),
("GRID",(0,0),(-1,-1),0.5,colors.HexColor("#e2e8f0")),
("BACKGROUND",(0,2),(-1,2),LIGHT_BLUE),
("BACKGROUND",(0,4),(-1,4),LIGHT_BLUE),
("ALIGN",(0,0),(-1,-1),"CENTER"),
("VALIGN",(0,0),(-1,-1),"MIDDLE"),
("TOPPADDING",(0,0),(-1,-1),4),("BOTTOMPADDING",(0,0),(-1,-1),4),
]))
story.append(terr_t)
story.append(Spacer(1, 0.3*cm))
story.append(Paragraph("ECG Examples", section_style))
story.append(two_ecg_row("stemi_anterior",
"Anterior STEMI β Tombstone ST elevation V2βV5, evolving Q waves (LAD occlusion)",
"stemi_inferior",
"Inferior STEMI β ST elevation in II, III, aVF; reciprocal depression in I and aVL"))
story.append(PageBreak())
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# DISEASE 2 β ATRIAL FIBRILLATION
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
story.append(section_header_table(2, "Atrial Fibrillation (AF)",
"Most common sustained cardiac arrhythmia β 5Γ increased stroke risk"))
story.append(Spacer(1, 0.3*cm))
story.append(Paragraph("Overview", section_style))
story.append(Paragraph(
"AF arises from chaotic, disorganised electrical activity in the atria, firing at 350β600 impulses "
"per minute. The AV node acts as a filter, resulting in an irregular ventricular response. "
"Loss of atrial contraction causes blood stasis in the left atrial appendage, increasing thromboembolic "
"risk β making anticoagulation a central management decision.", body_style))
story.append(Paragraph("ECG Features", section_style))
story += bullets([
"Complete absence of organised P waves β replaced by irregular fibrillatory (f) waves",
"Irregularly irregular R-R intervals β the pathognomonic hallmark",
"f-waves most visible in lead V1 and lead II",
"Narrow QRS complexes (unless aberrant conduction or WPW is present)",
"Ventricular rate 100β160 bpm if uncontrolled (rapid ventricular response)",
"Coarse AF: prominent f-waves (often valvular); Fine AF: subtle f-waves",
])
story.append(Spacer(1, 0.2*cm))
story.append(Paragraph("ECG Examples", section_style))
story.append(two_ecg_row("af_1",
"AF β absent P waves, irregular R-R intervals, coarse fibrillatory baseline",
"af_2",
"AF with Rapid Ventricular Response β narrow QRS tachycardia, no organised P waves"))
story.append(PageBreak())
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# DISEASE 3 β COMPLETE AV BLOCK
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
story.append(section_header_table(3, "Complete (Third-Degree) AV Block",
"Total failure of AV conduction β requires urgent pacemaker"))
story.append(Spacer(1, 0.3*cm))
story.append(Paragraph("Overview", section_style))
story.append(Paragraph(
"In complete AV block, no atrial impulses reach the ventricles due to total failure of the "
"AV conduction system. The atria and ventricles beat completely independently. A subsidiary "
"pacemaker (junctional or ventricular) maintains cardiac output at a slow escape rate. "
"Without treatment, patients may develop syncope or cardiac arrest. Permanent pacemaker "
"implantation is usually required.", body_style))
story.append(Paragraph("ECG Features", section_style))
story += bullets([
"Complete AV dissociation β P waves and QRS complexes are independent",
"Regular P-P intervals at a faster atrial rate; regular R-R intervals at a slower escape rate",
"P waves 'march through' QRS complexes and T waves without relationship",
"Junctional escape: narrow QRS at 40β60 bpm (block above or at His)",
"Ventricular escape: wide QRS at 20β40 bpm (block below His bundle) β more unstable",
"Wide QRS escape suggests infra-Hisian block β higher risk",
])
story.append(Spacer(1, 0.2*cm))
story.append(Paragraph("ECG Examples", section_style))
story.append(two_ecg_row("av_block_1",
"Complete AV Block β wide QRS ventricular escape, P waves dissociated, post-tricuspid valve surgery",
"av_block_2",
"Lead II rhythm strip β 3rd degree block with narrow junctional escape, P waves independent"))
story.append(PageBreak())
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# DISEASE 4 β VENTRICULAR TACHYCARDIA
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
story.append(section_header_table(4, "Ventricular Tachycardia (VT)",
"Rapid ventricular rhythm β can cause haemodynamic collapse"))
story.append(Spacer(1, 0.3*cm))
story.append(Paragraph("Overview", section_style))
story.append(Paragraph(
"VT is defined as β₯3 consecutive ventricular beats at a rate β₯100 bpm, originating below the "
"His bundle. Monomorphic VT has uniform QRS morphology; polymorphic VT has varying morphology. "
"Sustained VT (>30 seconds or causing haemodynamic compromise) is a medical emergency. "
"It most often occurs in the context of structural heart disease (prior MI, cardiomyopathy).", body_style))
story.append(Paragraph("ECG Features", section_style))
story += bullets([
"Wide QRS complexes (>120 ms, often β₯160 ms) at rapid rate (140β250 bpm)",
"AV dissociation β P waves present at slower, independent rate (pathognomonic)",
"Fusion beats and capture beats β highly specific for VT when present",
"Precordial concordance β all leads V1βV6 same direction (all positive or all negative)",
"Monomorphic VT: uniform QRS; Polymorphic VT: continuously varying QRS",
"Brugada/Vereckei criteria help differentiate VT from SVT with aberrancy",
])
story.append(Spacer(1, 0.2*cm))
story.append(Paragraph("ECG Example", section_style))
story.append(ecg_box("vt_1",
"Monomorphic VT β regular wide-complex tachycardia, positive concordance V1βV6, no visible P waves"))
story.append(PageBreak())
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# DISEASE 5 β LBBB
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
story.append(section_header_table(5, "Left Bundle Branch Block (LBBB)",
"New LBBB + chest pain = STEMI-equivalent until proven otherwise"))
story.append(Spacer(1, 0.3*cm))
story.append(Paragraph("Overview", section_style))
story.append(Paragraph(
"LBBB occurs when conduction through the left bundle is delayed or blocked. The left ventricle "
"depolarises slowly via cell-to-cell spread from the right. This causes characteristic wide, "
"abnormal QRS complexes and discordant repolarisation changes. New LBBB in the context of "
"acute chest pain is treated as STEMI-equivalent β Sgarbossa criteria help identify superimposed MI.", body_style))
story.append(Paragraph("ECG Features", section_style))
story += bullets([
"Broad QRS β₯120 ms (usually 140β160 ms in complete LBBB)",
"Broad, monophasic R waves in lateral leads (I, aVL, V5βV6) β often notched 'M-shaped'",
"Deep S wave or QS complex in right precordial leads (V1βV3)",
"Discordant ST-T changes β ST/T wave opposite to main QRS deflection",
"No septal Q waves in I, aVL, V5βV6 (septal activation reversed)",
"Sgarbossa criteria: concordant ST elevation β₯1 mm, concordant ST depression β₯1 mm in V1βV3, "
"or excessive discordant ST elevation >25% of S wave depth",
])
story.append(Spacer(1, 0.2*cm))
story.append(Paragraph("ECG Example", section_style))
story.append(ecg_box("lbbb_1",
"LBBB β broad QRS, monophasic R waves in I/aVL/V5βV6, deep S in V1βV3, discordant T-waves"))
story.append(PageBreak())
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# DISEASE 6 β RBBB
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
story.append(section_header_table(6, "Right Bundle Branch Block (RBBB)",
"Delayed right ventricular depolarisation β RSR' pattern in V1"))
story.append(Spacer(1, 0.3*cm))
story.append(Paragraph("Overview", section_style))
story.append(Paragraph(
"RBBB results from conduction delay in the right bundle branch. The right ventricle depolarises "
"late via slow cell-to-cell spread. RBBB can be a normal variant in young adults or may indicate "
"underlying pathology: right ventricular pressure overload (PE, pulmonary hypertension), atrial "
"septal defect, right heart ischaemia, or degenerative conduction disease.", body_style))
story.append(Paragraph("ECG Features", section_style))
story += bullets([
"Broad QRS β₯120 ms",
"RSR' ('rabbit-ear' or 'M-shaped') pattern in right precordial leads V1βV3 β hallmark feature",
"Tall, secondary R wave (R') in V1 representing delayed RV depolarisation",
"Wide, slurred S waves in lateral leads (I, aVL, V5βV6)",
"Secondary T-wave inversion in V1βV3 (discordant β normal finding in RBBB)",
"Axis may be rightward; right heart strain pattern may co-exist",
])
story.append(Spacer(1, 0.2*cm))
story.append(Paragraph("ECG Example", section_style))
story.append(ecg_box("rbbb_1",
"RBBB β rSR' 'rabbit-ear' in V1βV3 (blue arrows), broad slurred S in I/aVL/V5βV6 (red arrows), T-inversion V1βV3"))
story.append(PageBreak())
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# DISEASE 7 β HCM
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
story.append(section_header_table(7, "Hypertrophic Cardiomyopathy (HCM)",
"Leading cause of sudden cardiac death in young athletes"))
story.append(Spacer(1, 0.3*cm))
story.append(Paragraph("Overview", section_style))
story.append(Paragraph(
"HCM is an autosomal dominant genetic disorder (most commonly Ξ²-myosin heavy chain mutations) "
"causing disproportionate myocardial hypertrophy, typically asymmetric septal hypertrophy. "
"The hallmarks are diastolic dysfunction, dynamic LVOT obstruction, and myofiber disarray, "
"which creates an arrhythmogenic substrate. Most patients are asymptomatic but are at risk "
"for exertional syncope and sudden cardiac death from VF.", body_style))
story.append(Paragraph("ECG Features", section_style))
story += bullets([
"Left ventricular hypertrophy (LVH) voltage criteria β Sokolow-Lyon: S(V1) + R(V5/V6) β₯35 mm",
"Deep, widespread T-wave inversions β especially giant T-wave inversions in apical variant (Yamaguchi syndrome)",
"ST-segment depression with strain pattern in V4βV6",
"Absence of septal Q waves in lateral leads (despite hypertrophy)",
"Left axis deviation common; PR interval may be short",
"Abnormal ECG present in ~95% of HCM patients β normal ECG makes HCM unlikely",
])
story.append(Spacer(1, 0.2*cm))
story.append(Paragraph("ECG Examples", section_style))
story.append(two_ecg_row("hcm_1",
"HCM (Septal) β high-voltage R waves, ST depression, deep T-wave inversions, strain pattern",
"hcm_apical",
"Apical HCM (Yamaguchi) β giant T-wave inversions V2βV6, high QRS voltage"))
story.append(PageBreak())
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# DISEASE 8 β LONG QT
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
story.append(section_header_table(8, "Long QT Syndrome (LQTS)",
"Repolarisation disorder β QTc >500 ms risks Torsades de Pointes β VF"))
story.append(Spacer(1, 0.3*cm))
story.append(Paragraph("Overview", section_style))
story.append(Paragraph(
"LQTS is a disorder of cardiac ion channels causing prolonged ventricular repolarisation. "
"It may be inherited (LQT1: KCNQ1; LQT2: KCNH2; LQT3: SCN5A mutations) or acquired "
"(drugs, electrolyte disturbances). Prolonged repolarisation creates conditions for early "
"afterdepolarisations, triggering polymorphic VT (torsades de pointes β TdP), which can "
"degenerate into ventricular fibrillation and sudden death.", body_style))
story.append(Paragraph("ECG Features", section_style))
story += bullets([
"Prolonged QTc: males >440 ms, females >460 ms; risk of TdP when QTc >500 ms",
"Use Bazett formula: QTc = QT / β(RR interval in seconds)",
"LQT1: broad-based T waves; LQT2: notched/bifid T waves; LQT3: long isoelectric ST, then peaked T",
"T-wave alternans: beat-to-beat T-wave amplitude variation β marker of high arrhythmic risk",
"Torsades de Pointes: polymorphic VT with characteristic 'twisting' of QRS axis",
"Triggered by 'short-long-short' R-R sequence (PVC β compensatory pause β next beat triggers TdP)",
])
story.append(Spacer(1, 0.2*cm))
story.append(Paragraph("ECG Examples", section_style))
story.append(two_ecg_row("lqt_1",
"LQTS β QTc 680 ms, U waves (arrows in V1), progressing to polymorphic VT (torsades de pointes)",
"lqt_tdp",
"TdP initiation β short-long-short trigger, characteristic 'twisting' polymorphic VT"))
story.append(PageBreak())
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# DISEASE 9 β PERICARDITIS
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
story.append(section_header_table(9, "Acute Pericarditis",
"Pericardial inflammation β diffuse saddle-shaped ST elevation + PR depression"))
story.append(Spacer(1, 0.3*cm))
story.append(Paragraph("Overview", section_style))
story.append(Paragraph(
"Acute pericarditis is inflammation of the pericardium, most commonly viral in aetiology "
"(Coxsackievirus B, echovirus, adenovirus). It presents with sharp, pleuritic chest pain "
"that is worse supine and relieved by sitting forward (friction rub may be heard). "
"The ECG changes reflect current-of-injury from the adjacent inflamed epicardium across "
"the entire pericardial surface, producing characteristic diffuse changes distinct from STEMI.", body_style))
story.append(Paragraph("ECG Features (4 Evolutionary Stages)", section_style))
story += bullets([
"Stage 1 (hoursβdays): Diffuse concave ('saddle-shaped') ST elevation in nearly all leads except aVR and V1; PR segment depression most evident in lead II β most specific sign; reciprocal PR elevation in aVR",
"Stage 2 (days): ST elevation normalises; T waves flatten",
"Stage 3 (1β3 weeks): Diffuse T-wave inversions develop",
"Stage 4 (weeks): ECG normalises completely",
"Key differences from STEMI: diffuse distribution (not coronary territory), concave morphology, PR depression, no Q waves",
"Spodick's sign: downward slope of TP segment in multiple leads",
])
story.append(Spacer(1, 0.2*cm))
story.append(Paragraph("ECG Example", section_style))
story.append(ecg_box("pericarditis",
"Acute pericarditis β diffuse concave/saddle-shaped ST elevation, PR depression in II/III/aVF/V2βV6, ST depression and PR elevation in aVR"))
story.append(PageBreak())
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# DISEASE 10 β WPW
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
story.append(section_header_table(10, "Wolff-Parkinson-White (WPW) Syndrome",
"Accessory pathway pre-excitation β AF in WPW can be lethal"))
story.append(Spacer(1, 0.3*cm))
story.append(Paragraph("Overview", section_style))
story.append(Paragraph(
"WPW syndrome results from an accessory conduction pathway (Bundle of Kent) that bypasses the "
"AV node, allowing premature ventricular pre-excitation. The delta wave on ECG represents this "
"early activation. Patients are prone to re-entrant tachycardias (AVRT). Critically, if AF "
"develops, impulses can conduct rapidly over the accessory pathway without AV nodal protection "
"β ventricular rates of 200β300 bpm β ventricular fibrillation β sudden death. "
"Definitive treatment is catheter ablation of the accessory pathway.", body_style))
story.append(Paragraph("ECG Features", section_style))
story += bullets([
"Short PR interval (<120 ms) β AV node bypassed, impulse arrives early",
"Delta wave β slurred, gradual upstroke at the start of the QRS complex",
"Broad QRS (>120 ms) β combination of delta wave + normal His-Purkinje conduction (fusion beat)",
"Secondary discordant ST-T changes β opposite to main QRS deflection",
"Accessory pathway location estimated from delta wave polarity across leads",
"During orthodromic AVRT: narrow-complex regular tachycardia (most common)",
"During AF with WPW: wide, irregular, very rapid tachycardia β life-threatening; AVOID adenosine/verapamil/digoxin",
])
story.append(Spacer(1, 0.2*cm))
story.append(Paragraph("ECG Example", section_style))
story.append(ecg_box("wpw_1",
"WPW syndrome β short PR interval, delta waves (arrows on II/III), widened QRS, secondary ST-T changes; anteroseptal accessory pathway"))
story.append(PageBreak())
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# SUMMARY TABLE
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
story.append(Paragraph("Quick Reference Summary", ParagraphStyle("QR",
fontSize=18, fontName="Helvetica-Bold", textColor=BRAND_BLUE,
alignment=TA_CENTER, spaceAfter=8)))
story.append(HRFlowable(width="100%", thickness=1, color=ACCENT_BLUE))
story.append(Spacer(1, 0.3*cm))
def ph(t, bold=False, color=colors.white, size=9.5):
fn = "Helvetica-Bold" if bold else "Helvetica"
return Paragraph(f"<b>{t}</b>" if bold else t, ParagraphStyle("PH", fontSize=size,
fontName=fn, textColor=color, alignment=TA_CENTER, leading=13))
summary_data = [
[ph("#", bold=True), ph("Disease", bold=True), ph("Key ECG Finding", bold=True),
ph("Emergency?", bold=True)],
["1", "STEMI", "Convex ST elevation + Q waves in territory", "YES β Immediate PCI"],
["2", "Atrial Fibrillation", "No P waves Β· Irregularly irregular", "If rapid / unstable"],
["3", "Complete AV Block", "AV dissociation Β· Escape rhythm", "YES β Pacemaker"],
["4", "Ventricular Tachycardia", "Wide QRS tachycardia Β· AV dissociation", "YES β Cardioversion"],
["5", "LBBB", "Broad QRS Β· Dominant R in lateral leads", "If new + chest pain"],
["6", "RBBB", "RSR' in V1 Β· Slurred S in lateral leads", "Investigate cause"],
["7", "HCM", "LVH voltage + deep T-wave inversions", "Risk stratify for SCD"],
["8", "Long QT Syndrome", "QTc >500 ms β Torsades de Pointes", "If TdP / syncope"],
["9", "Acute Pericarditis", "Diffuse concave ST + PR depression", "Usually no"],
["10","WPW Syndrome", "Short PR + Delta wave + Wide QRS", "YES if AF with WPW"],
]
s_row_colors = [("BACKGROUND", (0,0), (-1,0), BRAND_BLUE)]
for i in range(1, len(summary_data)):
bg = LIGHT_BLUE if i % 2 == 0 else colors.white
s_row_colors.append(("BACKGROUND",(0,i),(-1,i),bg))
# highlight emergency rows
for i, row in enumerate(summary_data[1:], 1):
if "YES" in str(row[-1]):
s_row_colors.append(("TEXTCOLOR",(3,i),(3,i), ACCENT_RED))
s_row_colors.append(("FONTNAME",(3,i),(3,i),"Helvetica-Bold"))
s_table = Table(summary_data, colWidths=[0.7*cm, 4.5*cm, 7*cm, 4.5*cm])
s_table.setStyle(TableStyle([
("FONTNAME",(0,1),(-1,-1),"Helvetica"),
("FONTSIZE",(0,0),(-1,-1),9),
("TEXTCOLOR",(0,1),(-1,-1),BRAND_BLUE),
("ALIGN",(0,0),(-1,-1),"CENTER"),
("VALIGN",(0,0),(-1,-1),"MIDDLE"),
("GRID",(0,0),(-1,-1),0.5,colors.HexColor("#e2e8f0")),
("TOPPADDING",(0,0),(-1,-1),5),
("BOTTOMPADDING",(0,0),(-1,-1),5),
("LEFTPADDING",(0,0),(-1,-1),5),
] + s_row_colors))
story.append(s_table)
story.append(Spacer(1, 0.5*cm))
# Clinical pearl box
pearl_inner = Paragraph(
"<b>Clinical Pearl:</b> Always assess the ECG systematically β "
"<b>Rate β Rhythm β Axis β Intervals (PR, QRS, QTc) β ST-T changes</b>. "
"Pattern recognition is built on understanding the underlying electrophysiology of each condition. "
"A normal ECG does not exclude significant heart disease.",
ParagraphStyle("Pearl", fontSize=10, fontName="Helvetica",
textColor=BRAND_BLUE, leading=15, alignment=TA_JUSTIFY))
pearl_box = Table([[pearl_inner]], colWidths=[16.4*cm])
pearl_box.setStyle(TableStyle([
("BACKGROUND",(0,0),(-1,-1), colors.HexColor("#fffbeb")),
("BOX",(0,0),(-1,-1),1.5, colors.HexColor("#f59e0b")),
("TOPPADDING",(0,0),(-1,-1),10),
("BOTTOMPADDING",(0,0),(-1,-1),10),
("LEFTPADDING",(0,0),(-1,-1),12),
("RIGHTPADDING",(0,0),(-1,-1),12),
]))
story.append(pearl_box)
story.append(Spacer(1, 0.3*cm))
story.append(Paragraph(
"ECG images sourced from PMC Open Access Clinical VQA dataset and Harrison's Principles of Internal Medicine, 21st Ed. "
"Generated by Orris Medical AI Β· May 2026 Β· For educational purposes only.",
ParagraphStyle("Footer", fontSize=7.5, fontName="Helvetica-Oblique",
textColor=MID_GRAY, alignment=TA_CENTER)))
# ββ Build ββββββββββββββββββββββββββββββββββββββββββββββββββββββ
print("\nBuilding PDF...")
doc.build(story)
print(f"β PDF saved to: {OUTPUT}")
build_pdf()