Can u suggest me a best pdf for understanding complete ecg for different disorders

Searching the Web

best free PDF ECG interpretation complete guide disorders download

Here are the best ECG learning resources, ranging from beginner to advanced:

Top ECG PDF Resources

Free / Directly Downloadable PDFs

1. Introduction to ECG Interpretation - University of Utah (2023) An 80+ page, well-structured guide covering the 5-step method, normal ECG, arrhythmias, blocks, and MI patterns. Updated January 2023 - one of the best free academic PDFs available.
2. ECG Interpretation Made Incredibly Easy (Williams) Covers rhythms, blocks, bundle branch blocks, MI, hypertrophy, and electrolyte disorders with excellent illustrations. Easy to follow for beginners.
3. 150 ECG Problems - Hampton (IS MUNI) Practice-based approach with 150 real ECG cases covering a wide range of disorders. Best used after learning the basics.
4. 150 Practice ECGs: Interpretation and Review (ACC India) Similar case-based format from ACC India - good for systematic practice and self-testing.
5. Basic ECG Interpretation - Leonard (PRISMA Health) A concise clinical PDF focused on emergency ECG reading: STEMIs, heart blocks, WPW, AF. Good for quick clinical reference.

Recommended Books (Paid - Gold Standard)

BookBest For
The ECG Made Easy - Hamp tonAbsolute beginners
The ECG in Practice - Hamp tonIntermediate, clinical correlation
Marriott's Practical ElectrocardiographyAdvanced/comprehensive
ECG Interpretation: The Self-Assessment Approach - ThalerSelf-study with Q&A format
The Only EKG Book You'll Ever Need - ThalerPopular all-in-one text

Learning Path Suggestion

  1. Start with ECG Interpretation Made Incredibly Easy (free PDF above) to build foundations
  2. Move to University of Utah's 2023 guide for systematic 12-lead interpretation
  3. Practice with 150 ECG Problems (Hamp ton) to consolidate
  4. Use a free interactive tool like litfl.com/ecg-library for visual case-by-case practice - it covers virtually every ECG pattern disorder by disorder
This progression takes you from basics through real-world disorders including arrhythmias, conduction blocks, ischemia/MI, hypertrophy, pericarditis, and electrolyte/drug effects.

Create a summary PDF guide on ECG disorders & interpretation

Reading File
Running Command

mkdir -p /tmp/workspace/ecg-guide && ls /tmp/workspace/

Writing File

~/ecg-guide/create_ecg_pdf.py

from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.units import mm, cm
from reportlab.platypus import (
    SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
    HRFlowable, PageBreak, KeepTogether
)
from reportlab.platypus.tableofcontents import TableOfContents
from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_JUSTIFY
from reportlab.pdfgen import canvas
from reportlab.platypus import BaseDocTemplate, Frame, PageTemplate
import os

OUTPUT_PATH = "/tmp/workspace/ecg-guide/ECG_Complete_Guide.pdf"

# ── Colour palette ────────────────────────────────────────────────────────────
RED       = colors.HexColor("#C0392B")
DARK_RED  = colors.HexColor("#96281B")
NAVY      = colors.HexColor("#1A2E4A")
BLUE      = colors.HexColor("#2980B9")
LIGHT_BLUE= colors.HexColor("#D6EAF8")
TEAL      = colors.HexColor("#148F77")
LIGHT_TEAL= colors.HexColor("#D1F2EB")
ORANGE    = colors.HexColor("#E67E22")
LIGHT_ORANGE = colors.HexColor("#FDEBD0")
PURPLE    = colors.HexColor("#6C3483")
LIGHT_PURPLE = colors.HexColor("#E8DAEF")
GREEN     = colors.HexColor("#1E8449")
LIGHT_GREEN = colors.HexColor("#D5F5E3")
YELLOW_BG = colors.HexColor("#FEF9E7")
GRAY_LIGHT= colors.HexColor("#F2F3F4")
GRAY_MID  = colors.HexColor("#BFC9CA")
WHITE     = colors.white
BLACK     = colors.HexColor("#1C1C1C")

# ── Styles ────────────────────────────────────────────────────────────────────
styles = getSampleStyleSheet()

def style(name, **kw):
    s = ParagraphStyle(name, **kw)
    return s

TITLE_STYLE = style("MyTitle",
    fontName="Helvetica-Bold", fontSize=28, textColor=WHITE,
    alignment=TA_CENTER, spaceAfter=6, leading=34)

SUBTITLE_STYLE = style("MySubtitle",
    fontName="Helvetica", fontSize=13, textColor=colors.HexColor("#D6EAF8"),
    alignment=TA_CENTER, spaceAfter=4, leading=16)

CHAPTER_STYLE = style("Chapter",
    fontName="Helvetica-Bold", fontSize=16, textColor=WHITE,
    alignment=TA_LEFT, spaceAfter=4, spaceBefore=4, leading=20)

SECTION_STYLE = style("Section",
    fontName="Helvetica-Bold", fontSize=12, textColor=NAVY,
    alignment=TA_LEFT, spaceAfter=3, spaceBefore=6, leading=15)

SUBSECTION_STYLE = style("Subsection",
    fontName="Helvetica-Bold", fontSize=10.5, textColor=TEAL,
    alignment=TA_LEFT, spaceAfter=2, spaceBefore=4, leading=13)

BODY_STYLE = style("Body",
    fontName="Helvetica", fontSize=9.5, textColor=BLACK,
    alignment=TA_JUSTIFY, spaceAfter=3, leading=14)

BULLET_STYLE = style("Bullet",
    fontName="Helvetica", fontSize=9.5, textColor=BLACK,
    alignment=TA_LEFT, spaceAfter=2, leading=13,
    leftIndent=14, bulletIndent=4)

BOLD_BODY = style("BoldBody",
    fontName="Helvetica-Bold", fontSize=9.5, textColor=BLACK,
    alignment=TA_LEFT, spaceAfter=2, leading=13)

TABLE_HEADER = style("TH",
    fontName="Helvetica-Bold", fontSize=9, textColor=WHITE,
    alignment=TA_CENTER, leading=12)

TABLE_CELL = style("TC",
    fontName="Helvetica", fontSize=9, textColor=BLACK,
    alignment=TA_LEFT, leading=12)

TABLE_CELL_C = style("TCC",
    fontName="Helvetica", fontSize=9, textColor=BLACK,
    alignment=TA_CENTER, leading=12)

CAPTION_STYLE = style("Caption",
    fontName="Helvetica-Oblique", fontSize=8.5, textColor=colors.HexColor("#555555"),
    alignment=TA_CENTER, spaceAfter=4, leading=11)

WARNING_STYLE = style("Warning",
    fontName="Helvetica-Bold", fontSize=9.5, textColor=DARK_RED,
    alignment=TA_LEFT, spaceAfter=2, leading=13, leftIndent=10)

# ── Helper functions ──────────────────────────────────────────────────────────

def chapter_header(title, color=NAVY):
    """Returns a styled chapter header block."""
    tbl = Table([[Paragraph(title, CHAPTER_STYLE)]], colWidths=[170*mm])
    tbl.setStyle(TableStyle([
        ("BACKGROUND", (0,0), (-1,-1), color),
        ("ROUNDEDCORNERS", [4,4,4,4]),
        ("TOPPADDING", (0,0), (-1,-1), 7),
        ("BOTTOMPADDING", (0,0), (-1,-1), 7),
        ("LEFTPADDING", (0,0), (-1,-1), 12),
    ]))
    return tbl

def section_box(title, color=LIGHT_BLUE, tcolor=NAVY):
    s = style("sb", fontName="Helvetica-Bold", fontSize=11,
              textColor=tcolor, alignment=TA_LEFT, leading=14)
    tbl = Table([[Paragraph(title, s)]], colWidths=[170*mm])
    tbl.setStyle(TableStyle([
        ("BACKGROUND", (0,0), (-1,-1), color),
        ("TOPPADDING", (0,0), (-1,-1), 5),
        ("BOTTOMPADDING", (0,0), (-1,-1), 5),
        ("LEFTPADDING", (0,0), (-1,-1), 10),
        ("LINEBELOW", (0,0), (-1,-1), 1, tcolor),
    ]))
    return tbl

def info_box(text, bg=YELLOW_BG, border=ORANGE):
    s = style("ib", fontName="Helvetica", fontSize=9.5, textColor=BLACK,
              alignment=TA_LEFT, leading=13, leftIndent=4)
    tbl = Table([[Paragraph(text, s)]], colWidths=[165*mm])
    tbl.setStyle(TableStyle([
        ("BACKGROUND", (0,0), (-1,-1), bg),
        ("BOX", (0,0), (-1,-1), 1.2, border),
        ("TOPPADDING", (0,0), (-1,-1), 7),
        ("BOTTOMPADDING", (0,0), (-1,-1), 7),
        ("LEFTPADDING", (0,0), (-1,-1), 10),
        ("RIGHTPADDING", (0,0), (-1,-1), 10),
    ]))
    return tbl

def bullet(text, indent=14):
    return Paragraph(f"<bullet>&bull;</bullet> {text}", BULLET_STYLE)

def subbullet(text):
    s = style("sb2", fontName="Helvetica", fontSize=9, textColor=BLACK,
              alignment=TA_LEFT, leading=12, leftIndent=26, bulletIndent=16)
    return Paragraph(f"<bullet>&#9702;</bullet> {text}", s)

def sp(h=4):
    return Spacer(1, h*mm)

def hr(color=GRAY_MID, thickness=0.5):
    return HRFlowable(width="100%", thickness=thickness, color=color, spaceAfter=2*mm, spaceBefore=2*mm)

def make_table(headers, rows, col_widths, header_bg=NAVY, alt_bg=GRAY_LIGHT):
    header_row = [Paragraph(h, TABLE_HEADER) for h in headers]
    data = [header_row]
    for i, row in enumerate(rows):
        bg = WHITE if i % 2 == 0 else alt_bg
        data.append([Paragraph(str(c), TABLE_CELL) for c in row])
    tbl = Table(data, colWidths=col_widths)
    style_cmds = [
        ("BACKGROUND", (0,0), (-1,0), header_bg),
        ("ROWBACKGROUNDS", (0,1), (-1,-1), [WHITE, alt_bg]),
        ("GRID", (0,0), (-1,-1), 0.4, GRAY_MID),
        ("TOPPADDING", (0,0), (-1,-1), 5),
        ("BOTTOMPADDING", (0,0), (-1,-1), 5),
        ("LEFTPADDING", (0,0), (-1,-1), 6),
        ("RIGHTPADDING", (0,0), (-1,-1), 6),
        ("VALIGN", (0,0), (-1,-1), "MIDDLE"),
    ]
    tbl.setStyle(TableStyle(style_cmds))
    return tbl

# ── Page callbacks ────────────────────────────────────────────────────────────

def cover_page(canvas_obj, doc):
    canvas_obj.saveState()
    w, h = A4
    # Background gradient simulation with rectangles
    canvas_obj.setFillColor(NAVY)
    canvas_obj.rect(0, 0, w, h, fill=1, stroke=0)
    # Top accent bar
    canvas_obj.setFillColor(RED)
    canvas_obj.rect(0, h-12*mm, w, 12*mm, fill=1, stroke=0)
    # Bottom accent bar
    canvas_obj.setFillColor(TEAL)
    canvas_obj.rect(0, 0, w, 8*mm, fill=1, stroke=0)
    # ECG waveform decoration (simplified)
    canvas_obj.setStrokeColor(colors.HexColor("#E74C3C"))
    canvas_obj.setLineWidth(2)
    canvas_obj.setFillColor(colors.HexColor("#E74C3C"))
    # Draw a simple ECG trace across the page
    y_base = h * 0.42
    pts = [
        (10, y_base), (50, y_base), (60, y_base+5), (65, y_base-30),
        (70, y_base+60), (75, y_base-30), (82, y_base+8), (90, y_base),
        (130, y_base), (140, y_base+4), (148, y_base-12),
        (155, y_base+4), (165, y_base), (210, y_base),
        (220, y_base+4), (228, y_base-12), (235, y_base+4), (245, y_base),
        (290, y_base), (300, y_base+5), (308, y_base-30),
        (313, y_base+60), (318, y_base-30), (325, y_base+8), (333, y_base),
        (375, y_base), (385, y_base+4), (393, y_base-12),
        (400, y_base+4), (410, y_base), (455, y_base),
        (465, y_base+5), (473, y_base-30), (478, y_base+60),
        (483, y_base-30), (490, y_base+8), (500, y_base), (595, y_base),
    ]
    p = canvas_obj.beginPath()
    p.moveTo(pts[0][0], pts[0][1])
    for x, y in pts[1:]:
        p.lineTo(x, y)
    canvas_obj.drawPath(p, stroke=1, fill=0)
    canvas_obj.setStrokeColor(colors.HexColor("#2980B9"))
    canvas_obj.setLineWidth(0.5)
    canvas_obj.setFillColor(colors.HexColor("#2980B9"))
    y_base2 = y_base - 50
    pts2 = [
        (10, y_base2), (50, y_base2), (56, y_base2+3), (60, y_base2-18),
        (64, y_base2+36), (68, y_base2-18), (74, y_base2+5), (80, y_base2),
        (120, y_base2), (126, y_base2+3), (130, y_base2-18),
        (134, y_base2+36), (138, y_base2-18), (144, y_base2+5), (150, y_base2),
        (595, y_base2),
    ]
    p2 = canvas_obj.beginPath()
    p2.moveTo(pts2[0][0], pts2[0][1])
    for x, y in pts2[1:]:
        p2.lineTo(x, y)
    canvas_obj.drawPath(p2, stroke=1, fill=0)
    canvas_obj.restoreState()

def normal_page(canvas_obj, doc):
    canvas_obj.saveState()
    w, h = A4
    # Top bar
    canvas_obj.setFillColor(NAVY)
    canvas_obj.rect(0, h-10*mm, w, 10*mm, fill=1, stroke=0)
    # Header text
    canvas_obj.setFont("Helvetica-Bold", 8)
    canvas_obj.setFillColor(WHITE)
    canvas_obj.drawString(15*mm, h-6.5*mm, "ECG COMPLETE GUIDE: Disorders & Interpretation")
    canvas_obj.setFont("Helvetica", 8)
    canvas_obj.drawRightString(w-15*mm, h-6.5*mm, "Orris Medical Reference")
    # Bottom bar
    canvas_obj.setFillColor(NAVY)
    canvas_obj.rect(0, 0, w, 8*mm, fill=1, stroke=0)
    # Page number
    canvas_obj.setFont("Helvetica", 8)
    canvas_obj.setFillColor(WHITE)
    canvas_obj.drawCentredString(w/2, 2.8*mm, f"Page {doc.page}")
    # Left accent line
    canvas_obj.setFillColor(RED)
    canvas_obj.rect(0, 8*mm, 3*mm, h-18*mm, fill=1, stroke=0)
    canvas_obj.restoreState()

# ── Build content ─────────────────────────────────────────────────────────────

def build_pdf():
    doc = SimpleDocTemplate(
        OUTPUT_PATH,
        pagesize=A4,
        rightMargin=18*mm, leftMargin=22*mm,
        topMargin=18*mm, bottomMargin=16*mm,
        title="ECG Complete Guide: Disorders & Interpretation",
        author="Orris Medical Reference",
        subject="ECG Interpretation for Clinical Practice",
    )

    story = []

    # ═══════════════════════════════════════════════════════
    # COVER PAGE  (uses cover_page callback via first page)
    # ═══════════════════════════════════════════════════════
    story.append(Spacer(1, 45*mm))
    cover_title = style("ct", fontName="Helvetica-Bold", fontSize=34,
                        textColor=WHITE, alignment=TA_CENTER, leading=40)
    cover_sub   = style("cs", fontName="Helvetica", fontSize=15,
                        textColor=colors.HexColor("#AED6F1"), alignment=TA_CENTER, leading=20)
    cover_tag   = style("ctag", fontName="Helvetica-Oblique", fontSize=11,
                        textColor=colors.HexColor("#A9DFBF"), alignment=TA_CENTER, leading=14)

    story.append(Paragraph("ECG Complete Guide", cover_title))
    story.append(sp(3))
    story.append(Paragraph("Disorders &amp; Interpretation", cover_title))
    story.append(sp(8))
    story.append(Paragraph("A Systematic Reference for Medical Students, Nurses &amp; Clinicians", cover_sub))
    story.append(sp(35))
    story.append(Paragraph("Covering: Normal ECG · Arrhythmias · Conduction Blocks · Ischemia / MI", cover_tag))
    story.append(Paragraph("Hypertrophy · Electrolyte Disorders · Drug Effects · Systematic Approach", cover_tag))
    story.append(sp(10))
    cover_byline = style("cby", fontName="Helvetica", fontSize=10,
                         textColor=colors.HexColor("#85C1E9"), alignment=TA_CENTER, leading=13)
    story.append(Paragraph("Orris Medical Reference  |  2026", cover_byline))
    story.append(PageBreak())

    # ═══════════════════════════════════════════════════════
    # CHAPTER 1 – ECG Basics & Systematic Approach
    # ═══════════════════════════════════════════════════════
    story.append(chapter_header("Chapter 1: ECG Basics & Systematic Approach", NAVY))
    story.append(sp(3))

    story.append(section_box("What is an ECG?", LIGHT_BLUE, NAVY))
    story.append(sp(2))
    story.append(Paragraph(
        "An electrocardiogram (ECG/EKG) records the electrical activity of the heart "
        "over time via electrodes placed on the skin. A standard 12-lead ECG captures "
        "electrical vectors from 12 different angles, providing information about rhythm, "
        "conduction, ischemia, hypertrophy, and metabolic abnormalities.", BODY_STYLE))
    story.append(sp(3))

    story.append(section_box("ECG Paper & Measurements", LIGHT_TEAL, TEAL))
    story.append(sp(2))

    measurements = [
        ["Parameter", "Small Box", "Large Box", "Standard Value"],
        ["Time (horizontal)", "0.04 sec", "0.20 sec", "Paper speed: 25 mm/s"],
        ["Voltage (vertical)", "0.1 mV", "0.5 mV", "Calibration: 1 mV = 10 mm"],
        ["PR Interval", "—", "—", "0.12 – 0.20 sec (3–5 small boxes)"],
        ["QRS Duration", "—", "—", "< 0.12 sec (< 3 small boxes)"],
        ["QT Interval", "—", "—", "< 0.44 sec (corrected QTc)"],
        ["P Wave", "—", "—", "< 0.12 sec, < 2.5 mm height"],
    ]
    story.append(make_table(
        measurements[0], measurements[1:],
        [42*mm, 28*mm, 28*mm, 72*mm], TEAL))
    story.append(sp(3))

    story.append(section_box("12-Lead ECG: Lead Groups & Views", LIGHT_BLUE, NAVY))
    story.append(sp(2))

    lead_data = [
        ["Lead Group", "Leads", "Heart Region Viewed"],
        ["Inferior", "II, III, aVF", "Inferior wall (RCA territory)"],
        ["Lateral", "I, aVL, V5, V6", "Lateral wall (LCx territory)"],
        ["Anterior (Septal)", "V1, V2", "Interventricular septum"],
        ["Anterior (Anterior)", "V3, V4", "Anterior wall (LAD territory)"],
        ["Right-sided", "V1, V3R–V6R", "Right ventricle"],
        ["Posterior", "V7–V9 (or reciprocal V1–V3)", "Posterior wall"],
        ["Augmented Limb", "aVR", "Cavity / global ischemia detector"],
    ]
    story.append(make_table(lead_data[0], lead_data[1:], [32*mm, 42*mm, 96*mm], NAVY))
    story.append(sp(3))

    story.append(section_box("The 7-Step Systematic Approach", LIGHT_ORANGE, ORANGE))
    story.append(sp(2))

    steps = [
        ("<b>Step 1 – Rate:</b>", "Count R–R intervals. 300 ÷ number of large boxes between R waves. Normal: 60–100 bpm."),
        ("<b>Step 2 – Rhythm:</b>", "Regular or irregular? P before every QRS? QRS after every P?"),
        ("<b>Step 3 – P Wave:</b>", "Present, morphology, axis (upright in I & II = sinus). Duration & amplitude."),
        ("<b>Step 4 – PR Interval:</b>", "0.12–0.20 s. Short = WPW or junctional. Long = 1st degree block."),
        ("<b>Step 5 – QRS Complex:</b>", "Duration < 0.12 s. Check axis, bundle branch blocks, delta waves."),
        ("<b>Step 6 – ST Segment & T Wave:</b>", "Elevation or depression? T wave inversion, peaked, or biphasic?"),
        ("<b>Step 7 – QT Interval:</b>", "Correct with Bazett formula: QTc = QT / √RR. Normal QTc < 440 ms (men) / < 460 ms (women)."),
    ]
    for label, desc in steps:
        row_tbl = Table([[Paragraph(label, BOLD_BODY), Paragraph(desc, BODY_STYLE)]],
                        colWidths=[40*mm, 128*mm])
        row_tbl.setStyle(TableStyle([
            ("VALIGN", (0,0), (-1,-1), "TOP"),
            ("TOPPADDING", (0,0), (-1,-1), 3),
            ("BOTTOMPADDING", (0,0), (-1,-1), 3),
            ("LEFTPADDING", (0,0), (-1,-1), 4),
        ]))
        story.append(row_tbl)
    story.append(sp(2))
    story.append(info_box(
        "⚡  REMEMBER: Always check: rate · rhythm · P wave · PR · QRS · ST-T · QTc",
        YELLOW_BG, ORANGE))
    story.append(PageBreak())

    # ═══════════════════════════════════════════════════════
    # CHAPTER 2 – Normal ECG
    # ═══════════════════════════════════════════════════════
    story.append(chapter_header("Chapter 2: Normal ECG & Key Waveforms", TEAL))
    story.append(sp(3))

    story.append(section_box("Normal Sinus Rhythm Criteria", LIGHT_TEAL, TEAL))
    story.append(sp(2))
    nsr = [
        "Rate 60–100 bpm",
        "Regular rhythm (R–R intervals vary < 10%)",
        "Upright P wave in leads I, II, aVF",
        "Inverted P wave in aVR",
        "Constant PR interval (0.12–0.20 s)",
        "Every P followed by a QRS; every QRS preceded by a P",
        "QRS duration < 0.12 s (narrow complex)",
        "Normal axis: –30° to +90°",
    ]
    for b in nsr:
        story.append(bullet(b))
    story.append(sp(3))

    story.append(section_box("Normal Waveform Characteristics", LIGHT_BLUE, NAVY))
    story.append(sp(2))
    wave_data = [
        ["Wave / Segment", "Normal Features", "Clinical Significance"],
        ["P Wave", "< 0.12 s, < 2.5 mm, biphasic in V1", "Atrial depolarisation"],
        ["PR Segment", "Isoelectric, 0.12–0.20 s", "AV node conduction delay"],
        ["Q Wave", "< 0.04 s, < 25% of R height", "Septal or pathological (wide/deep)"],
        ["R Wave", "Progressive increase V1→V5", "Ventricular depolarisation"],
        ["S Wave", "Decreases V4→V6", "Late ventricular activation"],
        ["ST Segment", "Isoelectric (±0.5 mm in limb; ±1 mm in precordial)", "Repolarisation; elevation/depression = ischaemia"],
        ["T Wave", "Upright except aVR, V1 (±III); ≥ 1/8 but ≤ 2/3 of R", "Ventricular repolarisation"],
        ["U Wave", "Small positive deflection after T (best seen V2–V3)", "Often hypokalaemia; inverted = ischaemia"],
        ["QTc", "< 440 ms ♂, < 460 ms ♀", "Prolonged = risk of Torsades de Pointes"],
    ]
    story.append(make_table(wave_data[0], wave_data[1:], [38*mm, 58*mm, 74*mm], NAVY))
    story.append(sp(3))

    story.append(section_box("Axis Interpretation", LIGHT_PURPLE, PURPLE))
    story.append(sp(2))
    axis_data = [
        ["Axis", "Degrees", "Leads I & aVF", "Common Causes"],
        ["Normal", "−30° to +90°", "Both positive", "Normal"],
        ["Left Axis Deviation (LAD)", "−30° to −90°", "I +ve, aVF –ve", "LBBB, LAHB, inferior MI, LVH"],
        ["Right Axis Deviation (RAD)", "+90° to +180°", "I –ve, aVF +ve", "RBBB, RVH, lateral MI, PE"],
        ["Extreme / NW", "−90° to ±180°", "Both negative", "VT, severe emphysema, dextrocardia"],
    ]
    story.append(make_table(axis_data[0], axis_data[1:], [38*mm, 24*mm, 36*mm, 72*mm], PURPLE))
    story.append(PageBreak())

    # ═══════════════════════════════════════════════════════
    # CHAPTER 3 – Arrhythmias
    # ═══════════════════════════════════════════════════════
    story.append(chapter_header("Chapter 3: Arrhythmias", RED))
    story.append(sp(3))

    # ── Sinus arrhythmias
    story.append(section_box("3.1  Sinus Arrhythmias", LIGHT_BLUE, NAVY))
    story.append(sp(2))
    sinus_data = [
        ["Arrhythmia", "Rate", "Key ECG Feature", "Clinical Note"],
        ["Sinus Bradycardia", "< 60 bpm", "Normal P–QRS; slow rate", "Athletes, hypothyroidism, beta-blockers, vasovagal"],
        ["Sinus Tachycardia", "> 100 bpm", "Normal P–QRS; fast rate", "Pain, fever, PE, heart failure, anaemia, drugs"],
        ["Sinus Arrhythmia", "60–100 bpm", "Irregular R–R, varies with breathing", "Normal variant; prominent in young/athletes"],
        ["Sick Sinus Syndrome", "Variable", "Brady-tachy alternation, pauses > 2 s, SA block", "Requires pacemaker if symptomatic"],
    ]
    story.append(make_table(sinus_data[0], sinus_data[1:], [38*mm, 20*mm, 58*mm, 54*mm], RED))
    story.append(sp(3))

    # ── Supraventricular
    story.append(section_box("3.2  Supraventricular Arrhythmias", LIGHT_TEAL, TEAL))
    story.append(sp(2))
    sva_data = [
        ["Arrhythmia", "Rate", "Key ECG Features", "Notes"],
        ["PAC (Premature Atrial Complex)", "—", "Early P' wave, abnormal morphology, compensatory pause incomplete", "Benign; can trigger SVT/AF"],
        ["AVNRT (SVT)", "150–250 bpm", "Regular narrow QRS; P buried in / just after QRS (pseudo-r' in V1, pseudo-s in II)", "Most common paroxysmal SVT; vagal manoeuvres / adenosine"],
        ["AVRT (WPW-related SVT)", "150–300 bpm", "Narrow QRS (orthodromic) or wide (antidromic); pre-excitation during sinus = delta wave, short PR", "Avoid AV nodal agents in pre-excited AF"],
        ["Atrial Flutter", "Atrial 300 bpm; ventricular 150 bpm (2:1)", "Sawtooth flutter waves in II, III, aVF; regular ventricular response", "Ablation highly effective; anticoagulate"],
        ["Atrial Fibrillation (AF)", "Atrial 350–600 bpm; ventricular variable", "Irregularly irregular rhythm; absent P waves; fibrillatory baseline", "Most common sustained arrhythmia; stroke risk → CHA₂DS₂-VASc"],
        ["Multifocal AT (MAT)", "100–200 bpm", "Irregular; ≥ 3 distinct P wave morphologies; variable PR", "COPD, electrolyte disorders, elderly"],
        ["Junctional Rhythm", "40–60 bpm", "Absent or inverted P (before/during/after QRS); narrow QRS", "AV nodal escape; seen in inferior MI, digoxin toxicity"],
    ]
    story.append(make_table(sva_data[0], sva_data[1:], [40*mm, 22*mm, 58*mm, 50*mm], TEAL))
    story.append(sp(3))

    # ── Ventricular arrhythmias
    story.append(section_box("3.3  Ventricular Arrhythmias", LIGHT_ORANGE, ORANGE))
    story.append(sp(2))
    va_data = [
        ["Arrhythmia", "Rate", "Key ECG Features", "Management"],
        ["PVC (Premature Ventricular Complex)", "—", "Wide bizarre QRS ≥ 0.12 s; full compensatory pause; no P before", "Treat underlying cause; frequent PVCs (> 10,000/day) can cause CMP"],
        ["Accelerated Idioventricular Rhythm (AIVR)", "40–120 bpm", "Wide QRS, regular, no P preceding; often post-MI reperfusion", "Usually benign & self-limiting"],
        ["Ventricular Tachycardia (VT)", "> 100 bpm (usually 130–250)", "Wide complex tachycardia (QRS ≥ 0.12 s); AV dissociation, fusion / capture beats", "If pulse: amiodarone/cardioversion. Pulseless: defib"],
        ["Ventricular Fibrillation (VF)", "Chaotic", "Chaotic undulations; no identifiable QRS; no pulse", "Immediate defibrillation + CPR (ACLS)"],
        ["Torsades de Pointes (TdP)", "200–250 bpm", "Polymorphic VT; QRS twists around isoelectric axis; preceded by long QTc", "Magnesium IV; remove QT-prolonging drugs; overdrive pacing"],
        ["Brugada Syndrome", "—", "Coved-type ST elevation V1–V2; RBBB pattern; can trigger sudden VF", "ICD implantation; avoid sodium channel blockers"],
    ]
    story.append(make_table(va_data[0], va_data[1:], [42*mm, 22*mm, 60*mm, 46*mm], ORANGE))
    story.append(sp(2))
    story.append(info_box(
        "⚠  VT vs SVT with aberrancy: Brugada criteria, Vereckei algorithm. "
        "Key clues for VT: AV dissociation, fusion beats, concordance, extreme axis, QRS ≥ 0.16 s.",
        colors.HexColor("#FDEDEC"), DARK_RED))
    story.append(PageBreak())

    # ═══════════════════════════════════════════════════════
    # CHAPTER 4 – Conduction Blocks
    # ═══════════════════════════════════════════════════════
    story.append(chapter_header("Chapter 4: Conduction Blocks", PURPLE))
    story.append(sp(3))

    story.append(section_box("4.1  AV Blocks", LIGHT_PURPLE, PURPLE))
    story.append(sp(2))
    av_data = [
        ["Block", "PR Interval", "QRS Dropped?", "Rhythm", "Clinical Significance"],
        ["1st Degree AV Block", "> 0.20 s; constant", "No", "Regular", "Often benign; monitor; can be caused by vagal tone, inferior MI, drugs"],
        ["2nd Degree Mobitz I (Wenckebach)", "Progressive lengthening", "Yes – periodically", "Irregular grouped beating", "AV node dysfunction; usually benign; inferior MI; vagal"],
        ["2nd Degree Mobitz II", "Constant (normal or long)", "Yes – suddenly, without warning", "Regular except for dropped beats", "HIS/Purkinje disease; often progresses to complete heart block → pacemaker"],
        ["2:1 AV Block", "Constant", "Every alternate P dropped", "Regular at half atrial rate", "Cannot classify as Mobitz I or II without ≥ 3 P waves together; needs EP study"],
        ["3rd Degree (Complete) AV Block", "No relationship (AV dissociation)", "QRS unrelated to P waves; escape rhythm", "Atrial regular; ventricular regular but independent", "Emergency: junctional escape (narrow, 40–60) or ventricular escape (wide, < 40); requires pacing"],
    ]
    story.append(make_table(av_data[0], av_data[1:], [38*mm, 30*mm, 26*mm, 26*mm, 50*mm], PURPLE))
    story.append(sp(3))

    story.append(section_box("4.2  Bundle Branch Blocks", LIGHT_BLUE, NAVY))
    story.append(sp(2))

    story.append(Paragraph("<b>Right Bundle Branch Block (RBBB)</b>", SUBSECTION_STYLE))
    for b in [
        "QRS ≥ 0.12 s (complete RBBB) or 0.10–0.12 s (incomplete RBBB)",
        "rSR' ('M' pattern / rabbit ears) in V1–V2",
        "Wide, slurred S wave in I, aVL, V5–V6",
        "Secondary ST-T changes (ST depression + T inversion in V1–V3)",
        "Causes: normal variant, PE, RVH, ASD, anterior MI, post-cardiac surgery",
    ]:
        story.append(bullet(b))
    story.append(sp(2))

    story.append(Paragraph("<b>Left Bundle Branch Block (LBBB)</b>", SUBSECTION_STYLE))
    for b in [
        "QRS ≥ 0.12 s",
        "Broad, notched R wave ('M' shape) in I, aVL, V5–V6 (no septal Q waves)",
        "rS or QS pattern in V1–V3",
        "Discordant ST-T changes (opposite to main QRS deflection)",
        "New LBBB + chest pain = treat as STEMI equivalent (Sgarbossa criteria)",
        "Causes: IHD, dilated CMP, hypertension, aortic valve disease",
    ]:
        story.append(bullet(b))
    story.append(sp(2))

    story.append(Paragraph("<b>Fascicular Blocks (Hemiblocks)</b>", SUBSECTION_STYLE))
    fascicular_data = [
        ["Block", "Axis", "Lead I", "Lead II/III", "Notes"],
        ["Left Anterior Hemiblock (LAHB)", "LAD (−30° to −90°)", "+ve (qR)", "rS", "Most common; narrow QRS; no right axis shift"],
        ["Left Posterior Hemiblock (LPHB)", "RAD (+90° to +120°)", "rS", "+ve (qR)", "Rare; must exclude other RAD causes"],
        ["Bifascicular Block", "LAD", "RBBB + LAHB pattern", "—", "RBBB + LAHB; risk of complete block"],
        ["Trifascicular Block", "Variable", "1st-degree block + bifascicular", "—", "High risk of complete heart block; consider pacing"],
    ]
    story.append(make_table(fascicular_data[0], fascicular_data[1:], [40*mm, 24*mm, 24*mm, 24*mm, 58*mm], NAVY))
    story.append(PageBreak())

    # ═══════════════════════════════════════════════════════
    # CHAPTER 5 – Ischaemia & MI
    # ═══════════════════════════════════════════════════════
    story.append(chapter_header("Chapter 5: Ischaemia, Injury & Myocardial Infarction", DARK_RED))
    story.append(sp(3))

    story.append(section_box("5.1  Spectrum of Ischaemic ECG Changes", LIGHT_ORANGE, ORANGE))
    story.append(sp(2))
    isch_data = [
        ["Stage", "ECG Findings", "Pathophysiology"],
        ["Hyper-Acute (minutes)", "Tall, peaked 'hyperacute' T waves; ST straightening", "Acute transmural ischaemia; earliest sign"],
        ["Acute Injury (minutes–hours)", "ST elevation ≥ 1 mm in ≥ 2 contiguous leads (≥ 2 mm in V1–V4)", "Transmural injury current; STEMI criteria"],
        ["Established Infarction (hours–days)", "Pathological Q waves (≥ 0.04 s, ≥ 25% of R); ST normalises", "Myocyte necrosis; Q wave = dead tissue"],
        ["Chronic / Resolved (weeks–months)", "Persistent Q waves; T wave normalisation (may invert)", "Scar tissue; Q waves often permanent"],
        ["Subendocardial Ischaemia (NSTEMI/UA)", "Horizontal/downsloping ST depression ≥ 0.5 mm; T inversion", "Partial thickness ischaemia; no Q waves typically"],
    ]
    story.append(make_table(isch_data[0], isch_data[1:], [40*mm, 72*mm, 58*mm], ORANGE))
    story.append(sp(3))

    story.append(section_box("5.2  MI Localisation by Territory", LIGHT_BLUE, NAVY))
    story.append(sp(2))
    mi_data = [
        ["Territory", "Artery (Usual)", "Leads with ST Changes", "Reciprocal Changes"],
        ["Inferior MI", "RCA (85%) / LCx (15%)", "II, III, aVF", "I, aVL"],
        ["Anterior MI", "LAD (proximal)", "V1–V4 (or V1–V6)", "II, III, aVF (sometimes)"],
        ["Anteroseptal MI", "LAD (septal branches)", "V1–V3", "None specific"],
        ["Anterolateral MI", "LAD or LCx", "V1–V6, I, aVL", "II, III, aVF"],
        ["Lateral MI", "LCx / diagonal branch", "I, aVL, V5–V6", "V1–V3, II, III, aVF"],
        ["Posterior MI", "RCA / LCx", "V1–V3 ST depression; tall R wave", "ST elevation V7–V9 (posterior leads)"],
        ["Right Ventricular MI", "Proximal RCA", "V1 ST elevation; ST elevation V3R–V4R", "Usually with inferior MI (II, III, aVF)"],
        ["High Lateral / Apical", "Diagonal / OM branch", "I, aVL only (or none)", "II, III"],
    ]
    story.append(make_table(mi_data[0], mi_data[1:], [36*mm, 36*mm, 50*mm, 48*mm], DARK_RED))
    story.append(sp(3))

    story.append(section_box("5.3  Sgarbossa Criteria (MI in LBBB)", LIGHT_ORANGE, ORANGE))
    story.append(sp(2))
    sgarbossa = [
        ("<b>Criterion 1 (5 pts):</b>", "ST elevation ≥ 1 mm concordant with QRS direction"),
        ("<b>Criterion 2 (3 pts):</b>", "ST depression ≥ 1 mm in V1, V2, or V3 (concordant negative)"),
        ("<b>Criterion 3 (2 pts):</b>", "ST elevation ≥ 5 mm discordant with QRS direction"),
    ]
    for label, desc in sgarbossa:
        row = Table([[Paragraph(label, BOLD_BODY), Paragraph(desc, BODY_STYLE)]],
                    colWidths=[40*mm, 128*mm])
        row.setStyle(TableStyle([
            ("VALIGN", (0,0), (-1,-1), "TOP"),
            ("TOPPADDING", (0,0), (-1,-1), 3),
            ("LEFTPADDING", (0,0), (-1,-1), 4),
        ]))
        story.append(row)
    story.append(sp(1))
    story.append(info_box("Score ≥ 3 = high specificity for AMI. Modified Sgarbossa: use proportional criterion (ST/S ratio ≥ 0.25) instead of absolute 5 mm rule.", YELLOW_BG, ORANGE))
    story.append(PageBreak())

    # ═══════════════════════════════════════════════════════
    # CHAPTER 6 – Hypertrophy & Enlargement
    # ═══════════════════════════════════════════════════════
    story.append(chapter_header("Chapter 6: Chamber Hypertrophy & Enlargement", TEAL))
    story.append(sp(3))

    story.append(section_box("6.1  Ventricular Hypertrophy", LIGHT_TEAL, TEAL))
    story.append(sp(2))
    vh_data = [
        ["Criteria", "LVH", "RVH"],
        ["Voltage (main)", "Sokolow-Lyon: S(V1) + R(V5/V6) ≥ 35 mm\nCornell: R(aVL) ≥ 11 mm\nR(aVL) + S(V3) > 28 mm (M) / > 20 mm (F)", "R > S in V1; R in V1 ≥ 7 mm\nS in V5/V6 > 7 mm\nR:S ratio in V1 ≥ 1"],
        ["Axis", "LAD or normal", "RAD > +90°"],
        ["ST-T Changes", "Strain pattern: ST depression + T inversion in I, aVL, V5–V6", "Strain: ST depression + T inversion in V1–V4"],
        ["P Wave", "Often normal; may have P mitrale if raised LVEDP", "May show P pulmonale (tall P ≥ 2.5 mm)"],
        ["Causes", "HTN, aortic stenosis, HCM, AR", "Cor pulmonale, PE, pulmonary HTN, PS"],
    ]
    tbl = Table(
        [[Paragraph(vh_data[0][0], TABLE_HEADER), Paragraph(vh_data[0][1], TABLE_HEADER), Paragraph(vh_data[0][2], TABLE_HEADER)]] +
        [[Paragraph(r[0], TABLE_CELL), Paragraph(r[1], TABLE_CELL), Paragraph(r[2], TABLE_CELL)] for r in vh_data[1:]],
        colWidths=[30*mm, 72*mm, 68*mm]
    )
    tbl.setStyle(TableStyle([
        ("BACKGROUND", (0,0), (-1,0), TEAL),
        ("ROWBACKGROUNDS", (0,1), (-1,-1), [WHITE, LIGHT_TEAL]),
        ("GRID", (0,0), (-1,-1), 0.4, GRAY_MID),
        ("TOPPADDING", (0,0), (-1,-1), 5), ("BOTTOMPADDING", (0,0), (-1,-1), 5),
        ("LEFTPADDING", (0,0), (-1,-1), 6), ("RIGHTPADDING", (0,0), (-1,-1), 6),
        ("VALIGN", (0,0), (-1,-1), "TOP"),
    ]))
    story.append(tbl)
    story.append(sp(3))

    story.append(section_box("6.2  Atrial Enlargement", LIGHT_BLUE, NAVY))
    story.append(sp(2))
    atrial_data = [
        ["Feature", "Left Atrial Enlargement (LAE)", "Right Atrial Enlargement (RAE)"],
        ["P Wave Duration", "> 0.12 s (P mitrale)", "Normal or short"],
        ["P Wave Height", "Normal (or terminal negative portion in V1 > 1 mm × 1 mm box)", "≥ 2.5 mm in II, III, or aVF (P pulmonale)"],
        ["P Wave Morphology", "Bifid/notched P in II; biphasic in V1 with broad negative terminal", "Peaked, tall, narrow P"],
        ["Common Causes", "Mitral stenosis/regurgitation, HTN, LHF", "COPD, pulmonary HTN, tricuspid disease, RHF"],
    ]
    story.append(make_table(atrial_data[0], atrial_data[1:], [34*mm, 68*mm, 68*mm], NAVY))
    story.append(PageBreak())

    # ═══════════════════════════════════════════════════════
    # CHAPTER 7 – Electrolyte & Metabolic Disorders
    # ═══════════════════════════════════════════════════════
    story.append(chapter_header("Chapter 7: Electrolyte & Metabolic Disorders", colors.HexColor("#1A5276")))
    story.append(sp(3))

    story.append(section_box("7.1  Potassium Disorders", LIGHT_BLUE, NAVY))
    story.append(sp(2))
    k_data = [
        ["K+ Level", "ECG Changes", "Clinical Sequence"],
        ["Hypokalaemia\n< 3.5 mEq/L", "U wave prominence (V2–V3); T–U fusion; T wave flattening; QTU prolongation; ST depression; PVCs; TdP", "K 3–3.5: U waves → K 2.5–3: flat T, U > T → K < 2.5: T/U fusion, ST depression → K < 2: VF risk"],
        ["Hyperkalaemia\n> 5.5 mEq/L", "Peaked narrow T waves → PR prolongation → P wave flattening/disappearance → QRS widening → sine wave pattern → VF/asystole", "K 5.5–6.5: tall T → K 6.5–7.5: PR long, P flat → K > 7.5: wide QRS, sine wave → K > 9: cardiac arrest"],
    ]
    tbl_k = Table(
        [[Paragraph(k_data[0][0], TABLE_HEADER), Paragraph(k_data[0][1], TABLE_HEADER), Paragraph(k_data[0][2], TABLE_HEADER)]] +
        [[Paragraph(r[0], TABLE_CELL), Paragraph(r[1], TABLE_CELL), Paragraph(r[2], TABLE_CELL)] for r in k_data[1:]],
        colWidths=[30*mm, 72*mm, 68*mm]
    )
    tbl_k.setStyle(TableStyle([
        ("BACKGROUND", (0,0), (-1,0), NAVY),
        ("ROWBACKGROUNDS", (0,1), (-1,-1), [WHITE, LIGHT_BLUE]),
        ("GRID", (0,0), (-1,-1), 0.4, GRAY_MID),
        ("TOPPADDING", (0,0), (-1,-1), 6), ("BOTTOMPADDING", (0,0), (-1,-1), 6),
        ("LEFTPADDING", (0,0), (-1,-1), 6), ("RIGHTPADDING", (0,0), (-1,-1), 6),
        ("VALIGN", (0,0), (-1,-1), "TOP"),
    ]))
    story.append(tbl_k)
    story.append(sp(3))

    story.append(section_box("7.2  Other Electrolyte & Metabolic Disorders", LIGHT_TEAL, TEAL))
    story.append(sp(2))
    met_data = [
        ["Disorder", "ECG Findings", "Memory Aid"],
        ["Hypercalcaemia", "Short QT interval; short ST segment; prolonged PR; Osborn wave (rarely)", "Ca UP → QT DOWN"],
        ["Hypocalcaemia", "Prolonged QT (prolonged ST segment); T wave normal or inverted", "Ca DOWN → QT UP"],
        ["Hypomagnesaemia", "Prolonged QTc; T wave changes; TdP risk; similar to hypokalaemia", "Often coexists with hypokalaemia"],
        ["Hypothyroidism", "Sinus bradycardia; low voltage; prolonged QTc; T wave flattening/inversion; pericardial effusion pattern", "Think slow + low"],
        ["Hyperthyroidism", "Sinus tachycardia; AF; shortened QTc; high voltage", "Think fast + irregular"],
        ["Hypothermia", "Sinus bradycardia; Osborn (J) wave at QRS/ST junction; AF; prolonged intervals; VF risk", "Osborn waves pathognomonic"],
        ["Digoxin Effect", "Sagging ('reverse tick' or 'Salvador Dali moustache') ST depression; T wave inversion; shortened QT; PR prolongation", "ST scooping = digoxin effect (not toxicity)"],
        ["Digoxin Toxicity", "Any arrhythmia: PAT with block, regularised AF, VT/VF, junctional rhythms, PVCs (bigeminy)", "Classic: PAT with 2:1 block"],
    ]
    story.append(make_table(met_data[0], met_data[1:], [36*mm, 80*mm, 54*mm], TEAL))
    story.append(PageBreak())

    # ═══════════════════════════════════════════════════════
    # CHAPTER 8 – Special Conditions
    # ═══════════════════════════════════════════════════════
    story.append(chapter_header("Chapter 8: Special & Specific Conditions", colors.HexColor("#117A65")))
    story.append(sp(3))

    story.append(section_box("8.1  Pericarditis", LIGHT_TEAL, TEAL))
    story.append(sp(2))
    for b in [
        "<b>Saddle-shaped (concave up) diffuse ST elevation</b> in most leads except aVR and V1 (which show ST depression)",
        "<b>PR depression</b> – most specific finding; best seen in II and aVL",
        "<b>No reciprocal changes</b> (unlike MI) – a key differentiator",
        "<b>T wave inversion</b> occurs later (after ST normalises; Stage III)",
        "<b>Stages:</b> I (ST elevation, PR depression) → II (normalisation) → III (T inversion) → IV (normalisation)",
        "<b>Spodick's sign:</b> downsloping TP segment (best in II); sensitive early finding",
    ]:
        story.append(bullet(b))
    story.append(sp(3))

    story.append(section_box("8.2  Pulmonary Embolism (PE)", LIGHT_ORANGE, ORANGE))
    story.append(sp(2))
    for b in [
        "<b>Sinus tachycardia</b> – most common ECG finding in PE (present in ~40%)",
        "<b>S1Q3T3 pattern:</b> S wave in I + Q wave in III + T inversion in III (present in ~20% – not sensitive or specific alone)",
        "<b>Right heart strain:</b> new RBBB (complete or incomplete), RAD, right axis shift",
        "<b>T wave inversions V1–V4</b> – suggests RV strain; can mimic anterior ischaemia",
        "<b>AF or atrial flutter</b> may precipitate",
        "<b>Low voltage / sinus tachycardia alone</b> – should prompt CT-PA if clinically suspicious",
    ]:
        story.append(bullet(b))
    story.append(sp(3))

    story.append(section_box("8.3  Wolff-Parkinson-White (WPW) Syndrome", LIGHT_PURPLE, PURPLE))
    story.append(sp(2))
    for b in [
        "<b>Short PR interval</b> (< 0.12 s) – accessory pathway bypasses AV node",
        "<b>Delta wave</b> – slurred upstroke of QRS; initial slow conduction via accessory pathway",
        "<b>Wide QRS</b> (> 0.12 s) due to delta wave",
        "<b>Secondary ST-T changes</b> (discordant to QRS)",
        "<b>Risk:</b> AF with rapid ventricular response → VF if accessory pathway has short refractory period",
        "<b>AVOID:</b> Adenosine, digoxin, verapamil, beta-blockers in pre-excited AF (can accelerate conduction)",
    ]:
        story.append(bullet(b))
    story.append(sp(3))

    story.append(section_box("8.4  Long QT Syndromes", LIGHT_ORANGE, ORANGE))
    story.append(sp(2))
    lqt_data = [
        ["Type", "Gene", "Trigger", "T Wave Morphology"],
        ["LQT1 (KCNQ1)", "IKs loss of function", "Exercise / emotion", "Broad-based T wave"],
        ["LQT2 (KCNH2 / hERG)", "IKr loss of function", "Sudden sound / auditory startle", "Notched / bifid T wave"],
        ["LQT3 (SCN5A)", "INa gain of function", "Sleep / rest / bradycardia", "Long flat ST, late T wave"],
        ["Acquired (drugs, electrolytes)", "Multiple channels", "Drug initiation, hypokalaemia, hypomagnesaemia", "Variable QT prolongation"],
    ]
    story.append(make_table(lqt_data[0], lqt_data[1:], [38*mm, 36*mm, 44*mm, 52*mm], ORANGE))
    story.append(sp(2))
    story.append(info_box(
        "Common QT-prolonging drugs: antiarrhythmics (sotalol, amiodarone, quinidine), "
        "antibiotics (azithromycin, fluoroquinolones), antipsychotics (haloperidol, quetiapine), "
        "antidepressants (tricyclics, citalopram), antiemetics (ondansetron, metoclopramide).",
        YELLOW_BG, ORANGE))
    story.append(sp(3))

    story.append(section_box("8.5  Early Repolarisation vs. STEMI", LIGHT_GREEN, GREEN))
    story.append(sp(2))
    er_data = [
        ["Feature", "Early Repolarisation", "STEMI"],
        ["ST Morphology", "Concave (smiley face) upward", "Convex (frowning face) or flat upward"],
        ["Distribution", "Inferior and lateral leads; often widespread", "Localised to specific territory"],
        ["Reciprocal Changes", "Absent", "Present (key differentiator)"],
        ["J-Point Notching", "Characteristic notch or slur at J-point", "Usually absent"],
        ["Evolution", "Stable; no dynamic change", "Dynamic: evolving over time"],
        ["ST:T ratio (V6)", "< 0.25", "≥ 0.25 suggests STEMI/pericarditis"],
        ["Clinical Context", "Young, athletic, asymptomatic (usually)", "Chest pain, troponin rise, symptoms"],
    ]
    story.append(make_table(er_data[0], er_data[1:], [40*mm, 65*mm, 65*mm], GREEN))
    story.append(PageBreak())

    # ═══════════════════════════════════════════════════════
    # CHAPTER 9 – Quick Reference & Differentials
    # ═══════════════════════════════════════════════════════
    story.append(chapter_header("Chapter 9: Quick Reference & Clinical Differentials", NAVY))
    story.append(sp(3))

    story.append(section_box("ST Elevation Differential Diagnosis (STEMI Mimics)", LIGHT_ORANGE, ORANGE))
    story.append(sp(2))
    stemi_dd = [
        ["Cause", "Distinguishing ECG Feature", "Clinical Clue"],
        ["STEMI", "Convex ST elevation, reciprocal changes, evolution, Q waves", "Chest pain, troponin +ve"],
        ["Pericarditis", "Diffuse concave ST elevation, PR depression, no reciprocals", "Pleuritic pain, friction rub, viral prodrome"],
        ["Early Repolarisation", "Concave ST, J-point notch, stable, young", "Asymptomatic, athlete"],
        ["LBBB (new)", "Sgarbossa criteria; convex ST in concordant leads", "May need primary PCI if new LBBB + chest pain"],
        ["Brugada Pattern", "Coved ST in V1–V2 only, RBBB morphology", "Family history of sudden death, fever precipitates"],
        ["LVH Strain", "V5–V6 ST elevation possible; high voltage", "Hypertension, no acute symptoms"],
        ["Vasospasm / Prinzmetal", "Transient ST elevation, resolves with nitrates", "Occurs at rest, often at night"],
        ["Hyperkalaemia", "Wide QRS, peaked T, sine wave; may mimic STEMI", "Renal failure, peaked T waves universally"],
        ["Takotsubo (Stress) CMP", "Initial: ST elevation V1–V4; later: diffuse T inversion, QTc prolongation", "Emotional stress, post-menopausal women, apical ballooning"],
    ]
    story.append(make_table(stemi_dd[0], stemi_dd[1:], [36*mm, 66*mm, 68*mm], ORANGE))
    story.append(sp(3))

    story.append(section_box("Wide Complex Tachycardia (WCT) Differential", LIGHT_PURPLE, PURPLE))
    story.append(sp(2))
    wct_data = [
        ["Criterion", "Favours VT", "Favours SVT with Aberrancy"],
        ["AV Dissociation", "Present (P waves unrelated to QRS)", "Absent"],
        ["Fusion Beats", "Present", "Absent"],
        ["Capture Beats", "Present", "Absent"],
        ["QRS Duration", "> 0.16 s", "Usually < 0.14 s"],
        ["Axis", "Extreme NW axis (−90° to ±180°)", "LAD possible (LBBB morphology)"],
        ["Onset", "Abrupt; no initiating PAC", "Often initiated by PAC; RP < PR"],
        ["V Lead Concordance", "All positive or all negative V1–V6", "Mixed"],
        ["RS Nadir to S", "> 100 ms in any V lead (Brugada sign)", "< 100 ms"],
        ["Prior ECG", "Sinus ECG different from WCT morphology", "WCT matches pre-existing BBB"],
        ["History", "Structural heart disease strongly favours VT", "No structural heart disease"],
    ]
    story.append(make_table(wct_data[0], wct_data[1:], [40*mm, 70*mm, 60*mm], PURPLE))
    story.append(sp(3))

    story.append(section_box("Common Drug Effects on ECG", LIGHT_TEAL, TEAL))
    story.append(sp(2))
    drug_data = [
        ["Drug / Class", "ECG Effects", "Key Concern"],
        ["Digoxin (therapeutic)", "Scooping ST depression ('reverse tick'); short QT; PR prolongation; T inversion", "Toxicity: any arrhythmia; PAT with block classic"],
        ["Beta-Blockers", "Sinus bradycardia; PR prolongation; AV block", "Overdose: severe bradycardia, hypotension"],
        ["Calcium Channel Blockers (non-DHP)", "Sinus bradycardia; PR prolongation; AV block (verapamil > diltiazem)", "Overdose: fatal bradycardia; may cause AF"],
        ["Tricyclic Antidepressants (TCA)", "Wide QRS; prolonged QTc; right axis; deep S in I; tall R in aVR", "R:S ratio in aVR > 0.7 predicts seizure/arrhythmia"],
        ["Amiodarone", "Sinus bradycardia; prolonged PR, QRS, QTc; T wave changes; corneal microdeposits", "Pro-arrhythmic despite anti-arrhythmic classification"],
        ["Adenosine", "Transient AV block; brief asystole (1–3 s); sinus bradycardia", "Diagnostic/therapeutic in SVT; CI in WPW pre-excited AF"],
        ["Cocaine", "Sinus tachycardia; STEMI (coronary spasm); prolonged QTc; VT/VF", "Cocaine + beta-blocker = unopposed alpha → dangerous"],
    ]
    story.append(make_table(drug_data[0], drug_data[1:], [38*mm, 72*mm, 60*mm], TEAL))
    story.append(PageBreak())

    # ═══════════════════════════════════════════════════════
    # CHAPTER 10 – Summary Cheatsheet
    # ═══════════════════════════════════════════════════════
    story.append(chapter_header("Chapter 10: Rapid ECG Cheatsheet", RED))
    story.append(sp(3))

    story.append(section_box("One-Line Summaries – High-Yield for Exams & Clinics", LIGHT_BLUE, NAVY))
    story.append(sp(2))

    cheat_data = [
        ["Finding", "Diagnosis / Condition"],
        ["Irregularly irregular + no P waves", "Atrial Fibrillation"],
        ["Sawtooth waves at 300 bpm + regular ventricular response", "Atrial Flutter (2:1 block → 150 bpm)"],
        ["P buried in QRS or pseudo-r' V1 + pseudo-s II (narrow, fast)", "AVNRT (commonest SVT)"],
        ["Short PR + delta wave + wide QRS", "WPW (pre-excitation)"],
        ["Wide QRS > 0.12 s + rSR' in V1 + wide S in I, V6", "Right Bundle Branch Block (RBBB)"],
        ["Wide QRS + broad notched R in I/V6 + no septal Q waves", "Left Bundle Branch Block (LBBB)"],
        ["ST elevation + concave + PR depression + no reciprocals", "Pericarditis"],
        ["ST elevation + convex + reciprocal changes + Q waves", "STEMI"],
        ["Horizontal ST depression + T inversion (no Q waves)", "NSTEMI / Subendocardial ischaemia"],
        ["Progressive PR lengthening → dropped QRS (grouped beating)", "2nd degree AV block – Mobitz I (Wenckebach)"],
        ["Constant PR + sudden dropped QRS (no warning)", "2nd degree AV block – Mobitz II"],
        ["P waves unrelated to QRS + escape rhythm", "3rd degree (complete) AV block"],
        ["Peaked T → wide QRS → sine wave + hyperkalaemia history", "Hyperkalaemia"],
        ["Prominent U waves + flat T (U > T)", "Hypokalaemia"],
        ["Short QT interval", "Hypercalcaemia (or congenital short QT syndrome)"],
        ["Prolonged QT + polymorphic VT twisting axis", "Torsades de Pointes (Long QT)"],
        ["Coved ST elevation V1–V2 + RBBB morphology", "Brugada Syndrome"],
        ["S1Q3T3 + tachycardia + right heart strain", "Pulmonary Embolism (PE)"],
        ["Osborn (J) wave at J point + bradycardia + prolonged intervals", "Hypothermia"],
        ["Sagging ST ('reverse tick') + short QT + PR prolongation", "Digoxin Effect (therapeutic)"],
        ["Sokolow-Lyon voltage ≥ 35 mm + strain pattern", "Left Ventricular Hypertrophy (LVH)"],
        ["R > S in V1 + RAD + RV strain pattern", "Right Ventricular Hypertrophy (RVH)"],
        ["Diffuse T wave inversion + QTc prolongation + apical ballooning", "Takotsubo (Stress) Cardiomyopathy"],
        ["Delta + wide QRS + AF with rapid irregular wide QRS", "WPW + AF → risk of VF"],
        ["Wide complex tachycardia + AV dissociation + fusion beats", "Ventricular Tachycardia (VT) – proven"],
    ]
    story.append(make_table(cheat_data[0], cheat_data[1:],
                            [100*mm, 70*mm], DARK_RED, colors.HexColor("#FDEDEC")))
    story.append(sp(3))

    story.append(info_box(
        "📌  GOLDEN RULE: In any wide complex tachycardia with haemodynamic instability → "
        "treat as VT and cardiovert. Never delay treatment to achieve a diagnosis.",
        colors.HexColor("#FDEDEC"), DARK_RED))
    story.append(sp(4))

    story.append(info_box(
        "📚  Recommended Reading:\n"
        "• The ECG Made Easy – John Hampton (beginner)\n"
        "• The ECG in Practice – John Hampton (intermediate)\n"
        "• 150 ECG Problems – Hampton (case-based practice)\n"
        "• Marriott's Practical Electrocardiography (advanced)\n"
        "• LITFL ECG Library – litfl.com/ecg-library (free online, disorder-by-disorder)",
        LIGHT_TEAL, TEAL))

    # ── Final page ──
    story.append(sp(6))
    story.append(hr(NAVY, 1))
    footer_style = style("footer", fontName="Helvetica-Oblique", fontSize=8.5,
                         textColor=colors.HexColor("#555555"), alignment=TA_CENTER, leading=12)
    story.append(Paragraph(
        "This guide is intended as an educational reference only. "
        "Always correlate ECG findings with clinical history, physical examination, and other investigations. "
        "Orris Medical Reference | ECG Complete Guide | 2026",
        footer_style))

    # ── Build with page callbacks ──────────────────────────────────────────────
    doc.build(story,
              onFirstPage=cover_page,
              onLaterPages=normal_page)
    print(f"PDF created: {OUTPUT_PATH}")
    print(f"File size: {os.path.getsize(OUTPUT_PATH):,} bytes")

if __name__ == "__main__":
    build_pdf()
Running Command

cd /tmp/workspace/ecg-guide && python create_ecg_pdf.py

Your ECG guide PDF is ready! Download it here:

ECG Complete Guide

PDF Document · PDF


What's Inside (10 Chapters)

ChapterContent
1 - ECG BasicsPaper measurements, 12-lead views, 7-step systematic approach
2 - Normal ECGNSR criteria, waveform characteristics, axis interpretation
3 - ArrhythmiasSinus, supraventricular (AF, flutter, SVT, WPW), ventricular (VT, VF, TdP, Brugada)
4 - Conduction BlocksAV blocks (1st/2nd/3rd degree), RBBB, LBBB, hemiblocks, trifascicular
5 - Ischaemia & MIHyper-acute to chronic stages, MI localisation by territory, Sgarbossa criteria
6 - HypertrophyLVH, RVH, LAE, RAE with voltage criteria and strain patterns
7 - Electrolytes & MetabolicHypo/hyperkalaemia, calcium, magnesium, thyroid, hypothermia, digoxin
8 - Special ConditionsPericarditis, PE, WPW, Long QT syndromes, early repolarisation vs STEMI
9 - Quick ReferenceSTEMI mimics, WCT differential (VT vs SVT), drug effects on ECG
10 - Cheatsheet25 one-line high-yield summaries for exams and clinical practice
The PDF includes colour-coded chapter headers, formatted tables, info boxes with clinical pearls, and a decorative ECG trace on the cover page.
This is a shared conversation. Sign in to Orris to start your own chat.