incomplete right bundle branch block ECG rSR pattern V1

This diagnostic image is a 12-lead electrocardiogram (ECG) demonstrating sinus tachycardia with features of an incomplete right bundle branch block (RBBB). The tracing shows a regular rhythm with an elevated heart rate exceeding 100 beats per minute. Key morphological findings include an RSR' pattern in lead V1 and widened, slurred S waves in the lateral leads (I, aVL, V5, and V6), characteristic of delayed right ventricular conduction. The QRS duration is slightly prolonged. Significant ST-segment abnormalities are present, specifically ST depression in the inferior leads (II, III, and aVF) and ST elevation in lead aVR. These findings, in the clinical context of right ventricular strain, are often associated with acute pulmonary embolism. The PR and QT intervals appear within normal limits. This ECG serves as an educational example of conduction delays and secondary repolarization changes in the setting of acute cardiovascular pathology.

A standard 12-lead electrocardiogram (ECG) with rhythm strips (II, V1, V5) demonstrating complex cardiac conduction system disease. Key findings include a widened QRS complex (>120 ms) with an rSR' pattern in lead V1 and slurred S-waves in leads V5 and V6, diagnostic of a Right Bundle Branch Block (RBBB). The limb leads show a right axis deviation (negative QRS in lead I, positive in II and III), which, in combination with RBBB, is consistent with a bifascicular block (specifically RBBB and Left Posterior Fascicular Block). The rhythm strip in lead II is annotated with 'P' and 'V' to highlight atrioventricular dissociation features. It clearly demonstrates Second-degree AV block Mobitz Type II, evidenced by intermittent non-conducted P-waves ('dropped QRS') without progressive PR-interval lengthening. Additionally, the tracing shows a prolonged QTc interval. This ECG illustrates an incomplete trifascicular block, signifying advanced multi-fascicular disease and infra-Hisian conduction failure.

This diagnostic image is a standard 12-lead electrocardiogram (ECG) displayed on a red grid background, illustrating a regular sinus rhythm at approximately 76 beats per minute. The tracing demonstrates a consistent 1:1 P-wave to QRS complex relationship with normal P-wave morphology, confirming atrial and ventricular synchronization originating from the sinus node. Key findings include characteristics of an incomplete right bundle branch block (IRBBB). In precordial lead V1, a distinctive rsR' pattern is visible, characterized by an initial small positive 'r' wave, followed by a deeper 's' wave, and a secondary taller 'R'' wave. While the QRS duration is slightly widened compared to normal, it does not reach the full 120 ms threshold required for a complete bundle branch block. Additionally, slurred S waves are observed in the lateral leads (I, V5, V6), which are hallmark features of right-sided conduction delays. This ECG serves as an educational example of cardiac electrophysiology, specifically identifying minor intraventricular conduction disturbances within a normal sinus rhythm context.
left atrial enlargement ECG biphasic P wave V1

A 12-lead electrocardiogram (ECG) demonstrating a regular sinus rhythm with significant morphological abnormalities indicative of cardiac chamber enlargement. The tracing shows peaked, high-amplitude P waves (P pulmonale) most prominent in lead II, marked by red arrows, suggesting right atrial enlargement. Additionally, the biphasic P wave morphology in lead V1 with a prominent terminal negative component indicates concurrent left atrial enlargement, collectively representing bi-atrial enlargement. The QRS complexes exhibit high voltage in the precordial leads, specifically deep S waves in V1-V3 and tall R waves in V4-V6, meeting Sokolow-Lyon criteria for left ventricular hypertrophy (LVH). Secondary ST-segment and T-wave abnormalities (LV strain pattern) are visible in the lateral leads (I, aVL, V5-V6). The tracing is captured at a standard paper speed of 25 mm/sec and calibration of 10 mm/mV, serving as a classic educational example of multi-chamber enlargement secondary to chronic cardiac pressure or volume overload.

A standard 12-lead electrocardiogram (ECG) recorded at a paper speed of 25 mm/s. The tracing shows a normal sinus rhythm with notable atrial and ventricular repolarization abnormalities. The P waves in lead II demonstrate a notched or 'M-shaped' morphology (P-mitrale), while the P wave in lead V1 is prominently biphasic with a deep terminal negative component, both characteristic of left atrial enlargement. There is evidence of significant T-wave inversions across the precordial leads, specifically from V2 through V6, as well as in the inferior leads III and aVF. The QRS complexes appear to have normal voltage and duration, and the PR interval is within normal limits. This diagnostic image illustrates the electrical manifestations of structural heart changes, specifically left atrial pathology and diffuse repolarization abnormalities, commonly associated with conditions causing chronic pressure or volume overload.
| Parameter | Value |
|---|---|
| Heart Rate | 186 bpm |
| PR/RR interval | 140/323 ms |
| QRS Duration | 90 ms |
| QT/QTc | 134/235 ms |
| P-R-T axes | 123 / -8 / 168 |
| SV1/RV5/R+S | 0.55 / 1.64 / 2.19 |


Note: The machine diagnosis states "To be finally confirmed by physician." This ECG interpretation is educational and should be correlated with full clinical history, examination, and additional investigations. The note at the bottom of the ECG strip appears to reference GRBS > 300 mg/dl, suggesting the patient may be presenting with diabetic hyperglycemia - which itself is a common cause of sinus tachycardia.
Basics of pacemaker
pacemaker components pulse generator lead diagram cardiac pacing

Anatomical diagram illustrating the configuration for Prolonged Temporary Cardiac Pacing (PTCP). The illustration shows a human torso with a focus on the cardiovascular and pacing systems. A silver-colored, single-chamber pacemaker generator is positioned externally on the patient's upper right chest, secured to the skin. An active-fixation lead is connected to the generator and enters the venous system via a percutaneous puncture of the right internal jugular vein. The lead is shown descending through the superior vena cava, entering the heart, and terminating with its tip anchored in the apex of the right ventricle. The diagram highlights the clinical pathway for temporary pacing in patients with transient conduction disorders or those with contraindications to permanent implantation, such as active infection. Key components depicted include the external pulse generator, the transvenous lead, the jugular venous access site, and the intracardiac positioning within the right ventricular cavity.

This lateral chest X-ray of a pediatric patient demonstrates the components and configuration of an implanted epicardial pacemaker system. A radiopaque pulse generator is positioned inferiorly within the abdominal wall (rectus sheath). Radiopaque pacing leads extend superiorly from the generator, forming a large, redundant loop that encircles the cardiac silhouette. Notably, the leads follow a posterior course relative to the heart, rather than an anterior or mediastinal path. Two small metallic markers are visible at the leads' distal aspect near the mid-cardiac level. This specific configuration—where the epicardial lead loop completely wraps around the heart—is a diagnostic hallmark of cardiac strangulation, a rare complication where the growing heart is constricted by the non-expanding pacemaker leads. The image highlights the importance of lateral radiographic views in pediatric patients with epicardial pacemakers to assess for lead migration and potential compression of cardiac structures such as the pulmonary artery, atrioventricular groove, and coronary arteries.

This composite figure includes chest radiographs and a corresponding anatomical diagram illustrating an epicardial pacing system in a pediatric patient. Panel A (anteroposterior view) and Panel B (lateral view) show a radiopaque pacemaker pulse generator implanted in the upper abdominal region, specifically the rectus abdominis muscle sheath. Multiple leads emerge superiorly from the device. Labels indicate the right atrial lead (RAL), the ventricular anode (ANO) positioned on the anterior right ventricular wall, and the ventricular cathode (CAT) placed on the posterior left ventricular wall. A fourth lead (CRTL), intended for cardiac resynchronization therapy, is also visible on the anterior right ventricle. Panel C provides a simplified schematic diagram of the heart, detailing the epicardial attachment sites of these four leads. The radiographs additionally show surgical sternal wires and complex lead loops, which are characteristic of pediatric epicardial systems designed to accommodate growth. This educational visual demonstrates a complex pacing configuration for treating heart failure and left bundle branch block (LBBB) via cardiac resynchronization.
pacemaker ECG tracing pacing spike DDD VVI paced rhythm

This diagnostic image is a standard 12-lead electrocardiogram (ECG) printed on grid paper, demonstrating dual-chamber (DDD) cardiac pacing. The layout includes limb leads (I, II, III, aVR, aVL, aVF) and precordial leads (V1-V6) arranged in a multi-channel format, with a rhythm strip of Lead II at the bottom. The tracing clearly shows distinct vertical pacemaker spikes occurring in a sequential manner. The first spike (atrial spike) precedes the P-wave, followed by a second spike (ventricular spike) immediately preceding the QRS complex, indicating an AV-sequential pacing mode. The QRS complexes exhibit a widened morphology with a left bundle branch block-like pattern, particularly visible in the precordial leads, which is characteristic of right ventricular apical pacing. This ECG serves as an educational example for identifying artificial pacemaker activity, understanding dual-chamber pacing logic, and recognizing the morphology of paced cardiac rhythms in clinical practice.

This diagnostic image is an electrocardiogram (ECG) rhythm strip illustrating a ventricular-paced rhythm in the context of complete atrioventricular (AV) block. The tracing shows prominent, regular, sharp downward deflections representing paced QRS complexes, each preceded by a clear vertical pacemaker spike. The ventricular pacing rate is approximately 30 beats per minute. Between these paced complexes, the baseline demonstrates independent atrial activity with small-amplitude, irregular P waves at a faster rate of approximately 80 beats per minute, confirming AV dissociation. The paced QRS complexes exhibit an amplitude of approximately 15-20 mm (3-4 large grid squares). This tracing serves as a clinical example of pacemaker function (VVI mode) and the electrophysiological manifestations of hyperkalemia-induced T-wave oversensing or conduction disturbances, necessitating device interrogation and electrolyte correction.

An eighteen-channel surface electrocardiogram (ECG) tracing, including standard limb leads (I, II, III, aVR, aVL, aVF), precordial leads (V1-V6), and additional right-sided/posterior leads (V3R, V4R, V5R, V8, V9). The tracing demonstrates a transient failure of ventricular pacing capture in a patient with a dual-chamber pacemaker. Red arrows specifically indicate ventricular pacing spikes that are not followed by a QRS complex, indicating 'failure to capture.' In segments where capture is successful, a wide QRS complex follows the pacing spike, consistent with ventricular pacing. The underlying atrial rhythm shows P waves, suggesting atrial sensing or pacing (DDD mode). Leads V1 through V3 show predominantly negative QRS complexes with T-wave inversions, while the lateral leads show increasing R-wave amplitude. This diagnostic image illustrates a critical pacemaker malfunction, clinically relevant for identifying causes of syncope in paced patients, such as lead displacement, electrolyte imbalances, or increased myocardial thresholds due to underlying pathology like pulmonary embolism.
| Type | Generator Location | Electrode Location | Use |
|---|---|---|---|
| Transcutaneous | External (defibrillator unit) | Skin of chest wall | Emergency, temporary |
| Transvenous | External | Venous catheter tip in RV (±RA) | Temporary |
| Transesophageal | External | Esophagus | Atrial pacing only |
| Epicardial | External or internal | Epicardial surface (during surgery) | Post-op, pediatric |
| Permanent | Subcutaneous (prepectoral) | Venous or epicardial | Long-term management |
| Leadless | Intracardiac (RV itself) | Self-contained unit (e.g., Micra) | Permanent, no lead complications |
| Position | I | II | III | IV | V |
|---|---|---|---|---|---|
| Meaning | Chamber Paced | Chamber Sensed | Response to Sensing | Rate Modulation | Antitachycardia Function |
| Options | O, A, V, D | O, A, V, D | O, I, T, D | O, I, M, C, R | O, P, S, D |
| Mode | Meaning | Use |
|---|---|---|
| VOO | Ventricular pacing, no sensing, asynchronous | Fixed-rate; used with magnet |
| VVI | Ventricular paced, ventricular sensed, inhibited | AF with bradycardia; simple |
| AAI | Atrial paced, atrial sensed, inhibited | Sinus node dysfunction (intact AV node) |
| DDD | Dual paced + sensed, dual response | Most physiological; AV block + SSS |
| DDDR | DDD + rate modulation | Active patients |
In everyday clinical practice, only the first three letters are used.


| Feature | Details |
|---|---|
| Battery type | Lithium-iodine |
| Battery life | 7-12 years (permanent) |
| Default pacing rate | 60-80 bpm |
| Bipolar vs unipolar | Bipolar preferred (less interference) |
| Magnet effect | Converts to asynchronous (VOO/AOO/DOO) |
| Most physiologic mode | DDD |
| Most common malfunction | Lead displacement |
| CRT indication | HFrEF + LBBB + QRS ≥130 ms |
Generate a quick reference guide for ECG recognition of pacemaker malfunction
pacemaker failure to capture ECG spike no QRS

This diagnostic image shows a three-panel electrocardiogram (ECG) rhythm strip from lead II, demonstrating a progressive pacemaker malfunction characterized by loss of ventricular capture. Panel A illustrates effective ventricular pacing, where each pacing spike (marked by red arrows) is followed by a narrow QRS complex and T wave at a regular interval. Panel B shows emerging instability, with pacing spikes followed by inconsistent or altered QRS morphologies, suggesting intermittent capture. Panel C displays a frank loss of capture and pacing failure; several pacing spikes occur without subsequent QRS complexes, resulting in a slow, irregular underlying ventricular rhythm labeled as 'Irregular.' This sequence is a critical clinical illustration of pacemaker exit block or threshold elevation, which in this context is associated with acute myocardial infarction (AMI). The content is designed for intermediate to advanced medical learners focusing on cardiology, critical care, and rhythm strip interpretation.

An eighteen-channel surface electrocardiogram (ECG) tracing, including standard limb leads (I, II, III, aVR, aVL, aVF), precordial leads (V1-V6), and additional right-sided/posterior leads (V3R, V4R, V5R, V8, V9). The tracing demonstrates a transient failure of ventricular pacing capture in a patient with a dual-chamber pacemaker. Red arrows specifically indicate ventricular pacing spikes that are not followed by a QRS complex, indicating 'failure to capture.' In segments where capture is successful, a wide QRS complex follows the pacing spike, consistent with ventricular pacing. The underlying atrial rhythm shows P waves, suggesting atrial sensing or pacing (DDD mode). Leads V1 through V3 show predominantly negative QRS complexes with T-wave inversions, while the lateral leads show increasing R-wave amplitude. This diagnostic image illustrates a critical pacemaker malfunction, clinically relevant for identifying causes of syncope in paced patients, such as lead displacement, electrolyte imbalances, or increased myocardial thresholds due to underlying pathology like pulmonary embolism.
ls -la /tmp/workspace/ 2>/dev/null || mkdir -p /tmp/workspace && echo "workspace ready"
mkdir -p /tmp/workspace/pacemaker-guide && echo "dir created"
~/pacemaker-guide/generate_guide.py
"""
ECG Recognition of Pacemaker Malfunction - Quick Reference Guide
A printable A4 PDF card using reportlab
"""
from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.units import cm, mm
from reportlab.platypus import (
SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
HRFlowable, KeepTogether
)
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_JUSTIFY
from reportlab.platypus import FrameBreak
from reportlab.lib.colors import HexColor
import urllib.request
import os
# ── Color palette ────────────────────────────────────────────────────────────
NAVY = HexColor("#0D1B2A")
TEAL = HexColor("#1B7A8C")
TEAL_LIGHT = HexColor("#D4EEF2")
RED = HexColor("#C0392B")
RED_LIGHT = HexColor("#FADBD8")
ORANGE = HexColor("#E67E22")
ORANGE_LT = HexColor("#FDEBD0")
GREEN = HexColor("#1A7A4A")
GREEN_LT = HexColor("#D5F5E3")
PURPLE = HexColor("#6C3483")
PURPLE_LT = HexColor("#E8DAEF")
YELLOW_LT = HexColor("#FEF9E7")
GREY_LIGHT = HexColor("#F2F4F4")
GREY_MID = HexColor("#BDC3C7")
WHITE = colors.white
BLACK = colors.black
# ── Styles ───────────────────────────────────────────────────────────────────
styles = getSampleStyleSheet()
def make_style(name, parent="Normal", fontSize=9, leading=12, textColor=BLACK,
fontName="Helvetica", alignment=TA_LEFT, spaceAfter=2, spaceBefore=0,
leftIndent=0, rightIndent=0):
return ParagraphStyle(
name, parent=styles[parent],
fontSize=fontSize, leading=leading, textColor=textColor,
fontName=fontName, alignment=alignment,
spaceAfter=spaceAfter, spaceBefore=spaceBefore,
leftIndent=leftIndent, rightIndent=rightIndent
)
title_style = make_style("TitleStyle", fontSize=20, leading=24, textColor=WHITE,
fontName="Helvetica-Bold", alignment=TA_CENTER, spaceAfter=0)
subtitle_style = make_style("SubtitleStyle", fontSize=11, leading=14, textColor=TEAL_LIGHT,
fontName="Helvetica", alignment=TA_CENTER, spaceAfter=0)
section_hdr = make_style("SectionHdr", fontSize=11, leading=14, textColor=WHITE,
fontName="Helvetica-Bold", alignment=TA_LEFT, spaceAfter=0)
body = make_style("Body", fontSize=8.5, leading=12, textColor=NAVY)
body_sm = make_style("BodySm", fontSize=7.8, leading=10.5, textColor=NAVY)
bold_body = make_style("BoldBody", fontSize=8.5, leading=12, textColor=NAVY,
fontName="Helvetica-Bold")
caption = make_style("Caption", fontSize=7.5, leading=10, textColor=HexColor("#555555"),
alignment=TA_CENTER)
bullet = make_style("Bullet", fontSize=8.2, leading=11, textColor=NAVY,
leftIndent=10, spaceAfter=1)
red_label = make_style("RedLabel", fontSize=9.5, leading=12, textColor=RED,
fontName="Helvetica-Bold")
note_style = make_style("NoteStyle", fontSize=7.8, leading=10.5, textColor=NAVY,
fontName="Helvetica-Oblique")
footer_style = make_style("Footer", fontSize=7, leading=9, textColor=HexColor("#888888"),
alignment=TA_CENTER)
# ── Helper: colored section header bar ───────────────────────────────────────
def section_bar(text, color=TEAL, width=None):
"""Returns a single-cell table acting as a colored section header."""
t = Table([[Paragraph(text, section_hdr)]],
colWidths=[width or 17.6*cm])
t.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), 8),
("RIGHTPADDING", (0, 0), (-1, -1), 8),
]))
return t
def colored_cell(text, bg, fg=NAVY, bold=False, fontsize=8.5, padding=5):
fn = "Helvetica-Bold" if bold else "Helvetica"
st = ParagraphStyle("cc", fontSize=fontsize, leading=fontsize+2,
textColor=fg, fontName=fn)
t = Table([[Paragraph(text, st)]])
t.setStyle(TableStyle([
("BACKGROUND", (0, 0), (-1, -1), bg),
("TOPPADDING", (0, 0), (-1, -1), padding),
("BOTTOMPADDING", (0, 0), (-1, -1), padding),
("LEFTPADDING", (0, 0), (-1, -1), 6),
("RIGHTPADDING", (0, 0), (-1, -1), 6),
]))
return t
# ── Download ECG images ───────────────────────────────────────────────────────
IMG_DIR = "/tmp/workspace/pacemaker-guide/imgs"
os.makedirs(IMG_DIR, exist_ok=True)
images_to_fetch = {
"malfunction_trio.png":
"https://cdn.orris.care/cdss_images/fdb3454670a0ab5daa5bc87c6b563c792028bd47fceba5af2498d2af72076237.png",
"failure_capture.jpg":
"https://cdn.orris.care/cdss_images/pmc_clinical_VQA_7fa96862af2c988f3135d59e3c2faf859e716cd504bcaf7118f1064188ad2c42.jpg",
"ddd_pacing.jpg":
"https://cdn.orris.care/cdss_images/pmc_clinical_VQA_386706ec42e46f37d85b4b833ddd77362df92178ed6be6d9a226b7928fdc2a6d.jpg",
"vvi_pacing.jpg":
"https://cdn.orris.care/cdss_images/pmc_clinical_VQA_9cb295ec998044380d699e78d32cc5dda9c2a785f0f15c86acaa9d834b84d879.jpg",
"failure_capture2.jpg":
"https://cdn.orris.care/cdss_images/pmc_clinical_VQA_e3a2a7f855012cc42629d5a1dfcaccdb95c573c6b3ec7684c53d26c5a7d96a88.jpg",
}
for fname, url in images_to_fetch.items():
dest = os.path.join(IMG_DIR, fname)
if not os.path.exists(dest):
try:
urllib.request.urlretrieve(url, dest)
print(f" Downloaded: {fname}")
except Exception as e:
print(f" FAILED {fname}: {e}")
else:
print(f" Cached: {fname}")
print("Images ready.")
cd /tmp/workspace/pacemaker-guide && python generate_guide.py
~/pacemaker-guide/build_pdf.py
"""
ECG Recognition of Pacemaker Malfunction — Quick Reference PDF
Full 3-page A4 printable guide using reportlab
"""
from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.units import cm, mm
from reportlab.platypus import (
SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
HRFlowable, KeepTogether, Image
)
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_RIGHT
from reportlab.lib.colors import HexColor
import os
# ── Colours ──────────────────────────────────────────────────────────────────
NAVY = HexColor("#0D1B2A")
TEAL = HexColor("#1B7A8C")
TEAL_LIGHT = HexColor("#D4EEF2")
RED = HexColor("#C0392B")
RED_LIGHT = HexColor("#FADBD8")
ORANGE = HexColor("#E67E22")
ORANGE_LT = HexColor("#FDEBD0")
GREEN = HexColor("#1A7A4A")
GREEN_LT = HexColor("#D5F5E3")
PURPLE = HexColor("#6C3483")
PURPLE_LT = HexColor("#E8DAEF")
YELLOW_LT = HexColor("#FFFDE7")
GREY_LIGHT = HexColor("#F4F6F7")
GREY_BDR = HexColor("#CCD1D1")
WHITE = colors.white
BLACK = colors.black
# ── Styles ────────────────────────────────────────────────────────────────────
styles = getSampleStyleSheet()
def S(name, fontSize=9, leading=12, textColor=BLACK, fontName="Helvetica",
alignment=TA_LEFT, spaceAfter=2, spaceBefore=0, leftIndent=0):
return ParagraphStyle(name, parent=styles["Normal"],
fontSize=fontSize, leading=leading, textColor=textColor,
fontName=fontName, alignment=alignment,
spaceAfter=spaceAfter, spaceBefore=spaceBefore, leftIndent=leftIndent)
title_s = S("t", 22, 27, WHITE, "Helvetica-Bold", TA_CENTER)
sub_s = S("sub", 11, 15, TEAL_LIGHT, "Helvetica", TA_CENTER)
hdr_s = S("hdr", 10.5, 14, WHITE, "Helvetica-Bold")
body_s = S("body", 8.5, 12, NAVY)
body_sm = S("bsm", 7.8, 10.5, NAVY)
bold_s = S("bold", 8.5, 12, NAVY, "Helvetica-Bold")
bullet_s = S("bul", 8.2, 11.5, NAVY, leftIndent=10)
italic_s = S("ital", 8, 11, HexColor("#444444"), "Helvetica-Oblique")
cap_s = S("cap", 7.5, 10, HexColor("#555555"), alignment=TA_CENTER)
note_s = S("note", 7.5, 10, HexColor("#555555"), "Helvetica-Oblique")
footer_s = S("footer", 7, 9, HexColor("#888888"), alignment=TA_CENTER)
red_hdr = S("rhdr", 10, 13, RED, "Helvetica-Bold")
green_hdr = S("ghdr", 10, 13, GREEN, "Helvetica-Bold")
label_s = S("lbl", 8, 11, NAVY, "Helvetica-Bold")
PW = A4[0] - 2.4*cm # usable page width
IMG_DIR = "/tmp/workspace/pacemaker-guide/imgs"
def img(fname, width_cm=8.5):
p = os.path.join(IMG_DIR, fname)
if os.path.exists(p):
return Image(p, width=width_cm*cm, height=width_cm*cm*0.45)
return Paragraph(f"[image: {fname}]", italic_s)
# ── Helpers ───────────────────────────────────────────────────────────────────
def bar(text, bg=TEAL, width=None):
w = width or PW
t = Table([[Paragraph(text, hdr_s)]], colWidths=[w])
t.setStyle(TableStyle([
("BACKGROUND", (0,0),(-1,-1), bg),
("TOPPADDING", (0,0),(-1,-1), 6),
("BOTTOMPADDING", (0,0),(-1,-1), 6),
("LEFTPADDING", (0,0),(-1,-1), 8),
("RIGHTPADDING", (0,0),(-1,-1), 8),
]))
return t
def info_box(text, bg=TEAL_LIGHT, width=None):
w = width or PW
t = Table([[Paragraph(text, body_sm)]], colWidths=[w])
t.setStyle(TableStyle([
("BACKGROUND", (0,0),(-1,-1), bg),
("BOX", (0,0),(-1,-1), 0.5, GREY_BDR),
("TOPPADDING", (0,0),(-1,-1), 5),
("BOTTOMPADDING", (0,0),(-1,-1), 5),
("LEFTPADDING", (0,0),(-1,-1), 7),
("RIGHTPADDING", (0,0),(-1,-1), 7),
]))
return t
def two_col(left_items, right_items, lw=None, rw=None):
lw = lw or PW*0.50
rw = rw or PW - lw - 0.2*cm
left = [[item] for item in left_items]
right = [[item] for item in right_items]
tl = Table(left, colWidths=[lw])
tr = Table(right, colWidths=[rw])
tl.setStyle(TableStyle([("TOPPADDING",(0,0),(-1,-1),1),("BOTTOMPADDING",(0,0),(-1,-1),1)]))
tr.setStyle(TableStyle([("TOPPADDING",(0,0),(-1,-1),1),("BOTTOMPADDING",(0,0),(-1,-1),1)]))
outer = Table([[tl, tr]], colWidths=[lw, rw])
outer.setStyle(TableStyle([
("VALIGN",(0,0),(-1,-1),"TOP"),
("LEFTPADDING",(0,0),(-1,-1),0),
("RIGHTPADDING",(0,0),(-1,-1),0),
("TOPPADDING",(0,0),(-1,-1),0),
("BOTTOMPADDING",(0,0),(-1,-1),0),
]))
return outer
def malfunction_card(title, bg_hdr, bg_body, ecg_text, ecg_color,
bullets, causes, management, width=None):
w = width or PW
col_w = [w]
rows = []
rows.append([bar(title, bg=bg_hdr, width=w)])
ecg_st = S("ecg_"+title[:4], 8.5, 11.5, ecg_color, "Helvetica-Bold")
rows.append([Table([[Paragraph("🔍 ECG Finding:", label_s)], [Paragraph(ecg_text, ecg_st)]],
colWidths=[w],
style=[("BACKGROUND",(0,0),(-1,-1),bg_body),
("TOPPADDING",(0,0),(-1,-1),4),
("BOTTOMPADDING",(0,0),(-1,-1),4),
("LEFTPADDING",(0,0),(-1,-1),8),
("RIGHTPADDING",(0,0),(-1,-1),8)])])
cause_lines = "".join(f"• {c}<br/>" for c in causes)
mgmt_lines = "".join(f"• {m}<br/>" for m in management)
bullets_txt = "".join(f"• {b}<br/>" for b in bullets)
inner = Table([
[Paragraph("<b>Mechanism / Features:</b>", label_s)],
[Paragraph(bullets_txt, body_sm)],
[Paragraph("<b>Common Causes:</b>", label_s)],
[Paragraph(cause_lines, body_sm)],
[Paragraph("<b>Management:</b>", label_s)],
[Paragraph(mgmt_lines, body_sm)],
], colWidths=[w], style=[
("BACKGROUND",(0,0),(-1,-1), bg_body),
("TOPPADDING",(0,0),(-1,-1),2),
("BOTTOMPADDING",(0,0),(-1,-1),2),
("LEFTPADDING",(0,0),(-1,-1),8),
("RIGHTPADDING",(0,0),(-1,-1),8),
])
rows.append([inner])
card = Table(rows, colWidths=[w])
card.setStyle(TableStyle([
("BOX", (0,0),(-1,-1), 1, bg_hdr),
("TOPPADDING",(0,0),(-1,-1),0),
("BOTTOMPADDING",(0,0),(-1,-1),0),
("LEFTPADDING",(0,0),(-1,-1),0),
("RIGHTPADDING",(0,0),(-1,-1),0),
]))
return card
# ═══════════════════════════════════════════════════════════════════════════════
# BUILD PDF
# ═══════════════════════════════════════════════════════════════════════════════
OUT = "/tmp/workspace/pacemaker-guide/Pacemaker_ECG_Quick_Reference.pdf"
doc = SimpleDocTemplate(OUT, pagesize=A4,
leftMargin=1.2*cm, rightMargin=1.2*cm,
topMargin=1.2*cm, bottomMargin=1.2*cm)
story = []
SP = lambda n=4: Spacer(1, n)
HR = lambda: HRFlowable(width="100%", thickness=0.5, color=GREY_BDR, spaceAfter=4, spaceBefore=4)
# ─────────────────────────────────────────────────────────────────────────────
# PAGE 1 — Title + Overview + NBG Code + Normal Paced Rhythm
# ─────────────────────────────────────────────────────────────────────────────
# Title banner
title_tbl = Table(
[[Paragraph("ECG Recognition of Pacemaker Malfunction", title_s)],
[Paragraph("Quick Reference Guide · Cardiology & Emergency Medicine", sub_s)]],
colWidths=[PW])
title_tbl.setStyle(TableStyle([
("BACKGROUND", (0,0),(-1,-1), NAVY),
("TOPPADDING", (0,0),(-1,-1), 12),
("BOTTOMPADDING", (0,0),(-1,-1), 12),
("LEFTPADDING", (0,0),(-1,-1), 10),
("RIGHTPADDING", (0,0),(-1,-1), 10),
("ROUNDEDCORNERS",(0,0),(-1,-1), [5,5,5,5]),
]))
story.append(title_tbl)
story.append(SP(8))
# ── Section: Recognising a Paced Rhythm ──────────────────────────────────────
story.append(bar("1. Recognising a Normal Paced Rhythm", TEAL))
story.append(SP(3))
story.append(info_box(
"<b>Pacing Spike</b> = a sharp, narrow vertical deflection immediately preceding the paced chamber's complex. "
"Normal: <b>QRS ≥ 0.12 s (LBBB morphology)</b> after ventricular spike because RV apex is activated first.",
bg=TEAL_LIGHT))
story.append(SP(5))
# Two-column: Spike types + DDD image
spike_rows = [
[Paragraph("Pacing Mode", bold_s), Paragraph("ECG Appearance", bold_s)],
[Paragraph("Atrial only (AAI)", body_s), Paragraph("Spike → P wave → narrow QRS", body_s)],
[Paragraph("Ventricular only (VVI)", body_s), Paragraph("Spike → wide QRS (LBBB pattern)", body_s)],
[Paragraph("Dual chamber (DDD)", body_s), Paragraph("2 spikes: atrial spike→P, ventricular spike→wide QRS", body_s)],
[Paragraph("Biventricular (CRT)", body_s), Paragraph("Spike → narrowed QRS (resynchronised)", body_s)],
]
spike_tbl = Table(spike_rows, colWidths=[PW*0.38, PW*0.62])
spike_tbl.setStyle(TableStyle([
("BACKGROUND", (0,0),(-1,0), NAVY),
("TEXTCOLOR", (0,0),(-1,0), WHITE),
("FONTNAME", (0,0),(-1,0), "Helvetica-Bold"),
("ROWBACKGROUNDS",(0,1),(-1,-1),[WHITE, GREY_LIGHT]),
("BOX", (0,0),(-1,-1), 0.5, GREY_BDR),
("INNERGRID", (0,0),(-1,-1), 0.3, GREY_BDR),
("FONTSIZE", (0,0),(-1,-1), 8.5),
("TOPPADDING", (0,0),(-1,-1), 4),
("BOTTOMPADDING", (0,0),(-1,-1), 4),
("LEFTPADDING", (0,0),(-1,-1), 6),
]))
story.append(spike_tbl)
story.append(SP(5))
# DDD + VVI images side by side
ddd_img = img("ddd_pacing.jpg", 8.8)
vvi_img = img("vvi_pacing.jpg", 8.8)
img_tbl = Table(
[[ddd_img, vvi_img],
[Paragraph("DDD pacing — two spikes (atrial then ventricular)", cap_s),
Paragraph("VVI pacing — single ventricular spike, wide QRS", cap_s)]],
colWidths=[PW*0.50, PW*0.50])
img_tbl.setStyle(TableStyle([
("ALIGN", (0,0),(-1,-1), "CENTER"),
("VALIGN", (0,0),(-1,-1), "MIDDLE"),
("TOPPADDING", (0,0),(-1,-1), 2),
("BOTTOMPADDING", (0,0),(-1,-1), 2),
]))
story.append(img_tbl)
story.append(SP(8))
# ── Section: Quick-Look Checklist ─────────────────────────────────────────────
story.append(bar("2. Quick-Look ECG Checklist for Any Paced Patient", PURPLE))
story.append(SP(3))
checklist_rows = [
[Paragraph("Step", bold_s), Paragraph("Question to ask", bold_s), Paragraph("Normal finding", bold_s)],
[Paragraph("①", body_s), Paragraph("Is there a pacing spike?", body_s), Paragraph("Visible before each paced complex", body_s)],
[Paragraph("②", body_s), Paragraph("Does a P wave / QRS follow every spike?", body_s), Paragraph("Yes → capture is intact", body_s)],
[Paragraph("③", body_s), Paragraph("Is pacing rate appropriate (~60-80 bpm)?", body_s), Paragraph("No unexpected pauses or racing", body_s)],
[Paragraph("④", body_s), Paragraph("Any spike on T wave or in refractory period?", body_s), Paragraph("No → no undersensing", body_s)],
[Paragraph("⑤", body_s), Paragraph("Does the pacemaker stay quiet when native beats appear?", body_s), Paragraph("Yes → sensing is intact (demand mode)", body_s)],
[Paragraph("⑥", body_s), Paragraph("Are there unexpected pauses without spikes?", body_s), Paragraph("No → no oversensing / output failure", body_s)],
]
cl_tbl = Table(checklist_rows, colWidths=[PW*0.07, PW*0.51, PW*0.42])
cl_tbl.setStyle(TableStyle([
("BACKGROUND", (0,0),(-1,0), PURPLE),
("TEXTCOLOR", (0,0),(-1,0), WHITE),
("FONTNAME", (0,0),(-1,0), "Helvetica-Bold"),
("ROWBACKGROUNDS",(0,1),(-1,-1),[WHITE, PURPLE_LT]),
("BOX", (0,0),(-1,-1), 0.5, GREY_BDR),
("INNERGRID", (0,0),(-1,-1), 0.3, GREY_BDR),
("FONTSIZE", (0,0),(-1,-1), 8.5),
("TOPPADDING", (0,0),(-1,-1), 4),
("BOTTOMPADDING", (0,0),(-1,-1), 4),
("LEFTPADDING", (0,0),(-1,-1), 6),
("VALIGN", (0,0),(-1,-1), "TOP"),
]))
story.append(cl_tbl)
story.append(SP(8))
# ─────────────────────────────────────────────────────────────────────────────
# PAGE 2 — The Four Malfunctions
# ─────────────────────────────────────────────────────────────────────────────
from reportlab.platypus import PageBreak
story.append(PageBreak())
story.append(bar("3. The Four Pacemaker Malfunctions", NAVY))
story.append(SP(5))
card_w = PW
# ── 3A Failure to Capture ───────────────────────────────────────────────────
story.append(KeepTogether([
malfunction_card(
title="A. FAILURE TO CAPTURE",
bg_hdr=RED,
bg_body=RED_LIGHT,
ecg_text="Pacing spike present BUT NOT followed by a P wave / QRS complex. "
"Spike fires into isoelectric baseline. Native (narrow) QRS may appear after.",
ecg_color=RED,
bullets=[
"Spike fires on time; heart does NOT respond",
"Can be intermittent (every other beat) or complete",
"If ventricular: look for spike → flat line (no QRS) → possible escape beat",
],
causes=[
"Lead displacement or dislodgement (most common — early post-implant)",
"Lead fracture / conductor break",
"Elevated pacing threshold: ischaemia, fibrosis at electrode tip",
"Electrolyte disturbance: hyperkalaemia ↑ threshold",
"Exit block (progressive threshold rise)",
"Battery depletion / output set too low",
],
management=[
"Increase output (mA) — turn up current on generator",
"Check lead position (CXR, fluoroscopy)",
"Correct hyperkalaemia or acidosis urgently",
"If haemodynamically unstable: transcutaneous pacing bridge",
"Device interrogation + reprogramming or lead revision",
],
width=card_w),
]))
story.append(SP(5))
# Image: failure to capture
story.append(KeepTogether([
img("failure_capture.jpg", 17.2),
Paragraph("Failure to capture: pacing spikes visible (↑) not followed by QRS — bare baseline after spike. "
"Late panel shows complete loss of capture with slow escape rhythm. (Tintinalli / Braunwald)", cap_s),
]))
story.append(SP(7))
# ── 3B Failure to Sense (Undersensing) ─────────────────────────────────────
story.append(KeepTogether([
malfunction_card(
title="B. FAILURE TO SENSE (UNDERSENSING)",
bg_hdr=ORANGE,
bg_body=ORANGE_LT,
ecg_text="Pacing spike fires INSIDE or ON TOP OF a native P wave / QRS / T wave. "
"Spike does not respect the native cardiac cycle — fires at programmed rate regardless.",
ecg_color=ORANGE,
bullets=[
"Pacemaker ignores intrinsic activity and fires anyway",
"Competitive pacing with native rhythm",
"Dangerous: spike-on-T → R-on-T → risk of VF (especially with ventricular undersensing)",
"ECG: pacing spikes appear at fixed intervals cutting across native complexes",
],
causes=[
"Lead displacement into poor sensing zone",
"Lead fracture or insulation break",
"Sensitivity threshold programmed too high (less sensitive)",
"Small intrinsic signal amplitude (low-voltage P or QRS)",
"Electrolyte disturbance, ischaemia at sensing electrode",
],
management=[
"Increase sensitivity (lower mV threshold value = more sensitive)",
"Reposition lead if dislodged",
"Magnet application (→ asynchronous) if R-on-T risk is immediate",
"Device interrogation and reprogramming",
],
width=card_w),
]))
story.append(SP(7))
# ── 3C Failure to Pace (Oversensing) ────────────────────────────────────────
story.append(PageBreak())
story.append(KeepTogether([
malfunction_card(
title="C. FAILURE TO PACE (OVERSENSING)",
bg_hdr=GREEN,
bg_body=GREEN_LT,
ecg_text="Expected pacing spike is ABSENT — long pause with no spike despite rate dropping "
"below programmed lower rate limit. Underlying bradycardia or asystole visible.",
ecg_color=GREEN,
bullets=[
"Pacemaker senses a non-cardiac signal and misidentifies it as a QRS → inhibits itself",
"ECG: pauses longer than the programmed escape interval, no spike",
"Patient symptomatic: syncope, pre-syncope, dizziness",
"Distinguish from battery failure (rundown rate) and lead fracture",
],
causes=[
"Skeletal myopotentials (pectoral muscle activity, diaphragm)",
"Electromagnetic interference: MRI, electrocautery, TENS, arc welding",
"Broken pacemaker lead / conductor fracture creating noise",
"T-wave oversensing (pacemaker counts T wave as second QRS)",
"Sensitivity programmed too low (too sensitive)",
"Crosstalk in dual-chamber devices (atrial output sensed by ventricular lead)",
],
management=[
"Reduce sensitivity (raise mV threshold — less sensitive)",
"Remove source of electromagnetic interference",
"Magnet application → converts to asynchronous (VOO/AOO) — bypasses sensing",
"Reprogram refractory periods to avoid T-wave counting",
"Replace fractured lead",
],
width=card_w),
]))
story.append(SP(7))
# ── 3D Output Failure / No Spike ────────────────────────────────────────────
story.append(KeepTogether([
malfunction_card(
title="D. OUTPUT FAILURE (NO SPIKE — BATTERY / CIRCUIT)",
bg_hdr=PURPLE,
bg_body=PURPLE_LT,
ecg_text="No pacing spikes at all. Patient-dependent rhythm only — slow escape or asystole. "
"Differs from oversensing: no inhibiting signal to explain absence of spikes.",
ecg_color=PURPLE,
bullets=[
"Complete absence of pacing output — generator silent",
"ECG: no pacing spikes anywhere in the tracing",
"Underlying rhythm visible (junctional escape, idioventricular, or asystole)",
"Battery ERI/EOL: rate gradually slows before complete failure",
],
causes=[
"Battery depletion (end of life — ERI/EOL indicator reached)",
"Pulse generator circuit failure",
"Complete lead fracture / conductor break (open circuit)",
"Lead-connector disconnection from generator",
"Runaway pacemaker damage from external defibrillation",
],
management=[
"Transcutaneous pacing immediately if symptomatic/unstable",
"Device interrogation to confirm generator status",
"Check CXR: lead integrity, connector position",
"Urgent generator replacement",
"Atropine / isoproterenol as bridge (if AV conduction intact)",
],
width=card_w),
]))
story.append(SP(7))
# Malfunction trio image
story.append(KeepTogether([
img("malfunction_trio.png", 17.2),
Paragraph(
"A = Undersensing (spikes fire on native beats). "
"B = Oversensing (missing spikes → pauses, no output). "
"C = Failure to capture (spikes fire but no QRS follows). Source: Tintinalli's Emergency Medicine",
cap_s),
]))
story.append(SP(8))
# ─────────────────────────────────────────────────────────────────────────────
# PAGE 3 — Tachyarrhythmias, Magnet Use, Summary Table, Pearls
# ─────────────────────────────────────────────────────────────────────────────
story.append(PageBreak())
story.append(bar("4. Pacemaker-Associated Tachyarrhythmias", NAVY))
story.append(SP(4))
tach_rows = [
[Paragraph("Type", bold_s), Paragraph("Mechanism", bold_s), Paragraph("ECG", bold_s), Paragraph("Action", bold_s)],
[Paragraph("Pacemaker-Mediated\nTachycardia (PMT)", body_s),
Paragraph("PVC → retrograde atrial activation → sensed by atrial lead → triggers V pacing → endless loop", body_sm),
Paragraph("Regular tachycardia at upper rate limit (~120-160 bpm) with wide paced QRS; each QRS followed by retrograde P', then spike", body_sm),
Paragraph("Place magnet (breaks loop). Reprogram PVARP (post-ventricular atrial refractory period).", body_sm)],
[Paragraph("Rapid Atrial\nArrhythmia Tracking", body_s),
Paragraph("DDD device tracks AF/flutter at upper rate limit; 'mode-switch' not programmed", body_sm),
Paragraph("Regular paced QRS at upper rate limit (e.g. 130 bpm) with no visible P waves; may be irregular", body_sm),
Paragraph("Enable mode-switch. Rate-control AF. Consider AV nodal ablation.", body_sm)],
[Paragraph("Runaway Pacemaker", body_s),
Paragraph("Generator malfunction → fires at rates >200 bpm. Battery failure or external damage.", body_sm),
Paragraph("Very rapid regular paced complexes, often >200 bpm. Haemodynamic compromise.", body_sm),
Paragraph("Magnet may help. Urgent device interrogation. Generator replacement.", body_sm)],
[Paragraph("Lead-Induced\nVentricular Ectopy", body_s),
Paragraph("Irritation of RV endocardium by lead tip", body_sm),
Paragraph("Frequent PVCs with morphology matching paced QRS (LBBB pattern)", body_sm),
Paragraph("Lead repositioning. Antiarrhythmic if frequent.", body_sm)],
]
tach_tbl = Table(tach_rows, colWidths=[PW*0.17, PW*0.30, PW*0.30, PW*0.23])
tach_tbl.setStyle(TableStyle([
("BACKGROUND", (0,0),(-1,0), NAVY),
("TEXTCOLOR", (0,0),(-1,0), WHITE),
("FONTNAME", (0,0),(-1,0), "Helvetica-Bold"),
("ROWBACKGROUNDS",(0,1),(-1,-1),[WHITE, TEAL_LIGHT]),
("BOX", (0,0),(-1,-1), 0.5, GREY_BDR),
("INNERGRID", (0,0),(-1,-1), 0.3, GREY_BDR),
("FONTSIZE", (0,0),(-1,-1), 8),
("TOPPADDING", (0,0),(-1,-1), 4),
("BOTTOMPADDING", (0,0),(-1,-1), 4),
("LEFTPADDING", (0,0),(-1,-1), 5),
("VALIGN", (0,0),(-1,-1), "TOP"),
]))
story.append(tach_tbl)
story.append(SP(8))
# ── Section: Magnet Use ────────────────────────────────────────────────────────
story.append(bar("5. The Magnet Effect — When & Why", TEAL))
story.append(SP(4))
magnet_data = [
[Paragraph("What the magnet does", bold_s),
Paragraph("Closes reed switch → disables sensing → converts to ASYNCHRONOUS (VOO/AOO/DOO) mode at fixed 'magnet rate' (usually ~85-100 bpm in most devices).", body_sm)],
[Paragraph("Use when…", bold_s),
Paragraph("• Oversensing causing inhibition (patient bradycardic, no output)<br/>"
"• Electrocautery/surgery causing inhibition<br/>"
"• Pacemaker-mediated tachycardia<br/>"
"• Runaway pacemaker (attempt)", body_sm)],
[Paragraph("Do NOT use when…", bold_s),
Paragraph("• Undersensing (magnet makes it worse — more asynchronous firing, R-on-T risk)<br/>"
"• Failure to capture (magnet won't fix output threshold problem)<br/>"
"• ICD — magnet suspends anti-tachycardia therapy (shocks), not pacing mode", body_sm)],
[Paragraph("ICD vs pacemaker", bold_s),
Paragraph("On an ICD: magnet suspends shock delivery only. Pacing function is NOT converted to asynchronous — behaviour differs from a pure pacemaker.", body_sm)],
]
magnet_tbl = Table(magnet_data, colWidths=[PW*0.20, PW*0.80])
magnet_tbl.setStyle(TableStyle([
("ROWBACKGROUNDS",(0,0),(-1,-1),[TEAL_LIGHT, WHITE]),
("BOX", (0,0),(-1,-1), 0.5, TEAL),
("INNERGRID",(0,0),(-1,-1), 0.3, GREY_BDR),
("TOPPADDING", (0,0),(-1,-1), 5),
("BOTTOMPADDING", (0,0),(-1,-1), 5),
("LEFTPADDING", (0,0),(-1,-1), 6),
("VALIGN", (0,0),(-1,-1), "TOP"),
("FONTSIZE",(0,0),(-1,-1), 8.5),
]))
story.append(magnet_tbl)
story.append(SP(8))
# ── Section: At-a-Glance Summary ──────────────────────────────────────────────
story.append(bar("6. At-a-Glance Comparison Table", NAVY))
story.append(SP(4))
summary_rows = [
[Paragraph("Malfunction", bold_s),
Paragraph("Spike Present?", bold_s),
Paragraph("Capture?", bold_s),
Paragraph("Sensing?", bold_s),
Paragraph("ECG Clue", bold_s),
Paragraph("1st Action", bold_s)],
[Paragraph("Failure to Capture", body_s),
Paragraph("✓ YES", body_s), Paragraph("✗ NO", body_s), Paragraph("Normal", body_s),
Paragraph("Spike → isoelectric line, no QRS", body_s),
Paragraph("↑ output (mA)", body_s)],
[Paragraph("Undersensing", body_s),
Paragraph("✓ YES (wrong time)", body_s), Paragraph("May occur", body_s), Paragraph("✗ POOR", body_s),
Paragraph("Spike inside native QRS or on T wave", body_s),
Paragraph("↑ sensitivity", body_s)],
[Paragraph("Oversensing", body_s),
Paragraph("✗ ABSENT", body_s), Paragraph("N/A", body_s), Paragraph("✗ TOO MUCH", body_s),
Paragraph("Pauses — no spike despite low rate", body_s),
Paragraph("Magnet; ↓ sensitivity", body_s)],
[Paragraph("Output Failure", body_s),
Paragraph("✗ NONE", body_s), Paragraph("✗ NO", body_s), Paragraph("N/A", body_s),
Paragraph("No spikes anywhere", body_s),
Paragraph("Transcutaneous pacing", body_s)],
[Paragraph("PMT", body_s),
Paragraph("✓ YES (rapid)", body_s), Paragraph("✓ YES", body_s), Paragraph("↑ (retrograde)", body_s),
Paragraph("Regular tachycardia at upper rate limit", body_s),
Paragraph("Magnet", body_s)],
]
sum_tbl = Table(summary_rows, colWidths=[PW*0.17, PW*0.13, PW*0.10, PW*0.10, PW*0.30, PW*0.20])
sum_tbl.setStyle(TableStyle([
("BACKGROUND", (0,0),(-1,0), NAVY),
("TEXTCOLOR", (0,0),(-1,0), WHITE),
("FONTNAME", (0,0),(-1,0), "Helvetica-Bold"),
("ROWBACKGROUNDS",(0,1),(-1,-1),[WHITE, GREY_LIGHT]),
("BACKGROUND", (0,1),(-1,1), RED_LIGHT),
("BACKGROUND", (0,2),(-1,2), ORANGE_LT),
("BACKGROUND", (0,3),(-1,3), GREEN_LT),
("BACKGROUND", (0,4),(-1,4), PURPLE_LT),
("BACKGROUND", (0,5),(-1,5), TEAL_LIGHT),
("BOX", (0,0),(-1,-1), 0.5, GREY_BDR),
("INNERGRID", (0,0),(-1,-1), 0.3, GREY_BDR),
("FONTSIZE", (0,0),(-1,-1), 8),
("TOPPADDING", (0,0),(-1,-1), 4),
("BOTTOMPADDING", (0,0),(-1,-1), 4),
("LEFTPADDING", (0,0),(-1,-1), 5),
("VALIGN", (0,0),(-1,-1), "TOP"),
]))
story.append(sum_tbl)
story.append(SP(8))
# ── Clinical Pearls ────────────────────────────────────────────────────────────
story.append(bar("7. Clinical Pearls", TEAL))
story.append(SP(4))
pearls = [
("<b>Spike ON T wave = Undersensing</b> — most dangerous as R-on-T can trigger VF in ventricle.",),
("<b>Paced LBBB morphology is normal</b> — wide QRS from RV apex pacing is expected; new RBBB pattern suggests lead migration to LV.",),
("<b>Pseudofusion</b> — spike fires just before intrinsic QRS; complex looks hybrid (partially paced). Not a malfunction.",),
("<b>Fusion beats</b> — native and paced activation merge; QRS morphology between native and fully paced. Also normal.",),
("<b>Hyperkalaemia = silent killer of pacemakers</b> — raises threshold → failure to capture; first ECG sign may be peaked T waves + loss of capture.",),
("<b>Post-cardiac surgery</b> — temporary epicardial wires common; threshold rises over days, check capture daily.",),
("<b>Always check CXR</b> in any pacemaker malfunction — lead fracture, displacement, pneumothorax, and pericardial effusion (cardiac tamponade) all visible.",),
("<b>Interrogate the device</b> — the programmer provides exact parameters, threshold measurements, lead impedance, and battery status. Always call the manufacturer rep if in doubt.",),
]
pearl_data = [[Paragraph(f"★ {p[0]}", body_sm)] for p in pearls]
pearl_tbl = Table(pearl_data, colWidths=[PW])
pearl_tbl.setStyle(TableStyle([
("ROWBACKGROUNDS",(0,0),(-1,-1),[YELLOW_LT, WHITE]),
("BOX", (0,0),(-1,-1), 0.5, ORANGE),
("INNERGRID",(0,0),(-1,-1), 0.3, GREY_BDR),
("TOPPADDING", (0,0),(-1,-1), 4),
("BOTTOMPADDING", (0,0),(-1,-1), 4),
("LEFTPADDING", (0,0),(-1,-1), 8),
]))
story.append(pearl_tbl)
story.append(SP(8))
# ── Footer ─────────────────────────────────────────────────────────────────────
story.append(HRFlowable(width="100%", thickness=0.5, color=GREY_BDR, spaceAfter=4))
story.append(Paragraph(
"Sources: Roberts and Hedges' Clinical Procedures in Emergency Medicine · Tintinalli's Emergency Medicine · "
"Rosen's Emergency Medicine · Braunwald's Heart Disease · Harrison's Principles of Internal Medicine 22E · "
"For educational purposes only — confirm with device interrogation and senior cardiology review.",
footer_s))
# ── Build ─────────────────────────────────────────────────────────────────────
doc.build(story)
print(f"PDF created: {OUT}")
cd /tmp/workspace/pacemaker-guide && python build_pdf.py 2>&1
ls -lh /tmp/workspace/pacemaker-guide/Pacemaker_ECG_Quick_Reference.pdf
Pacemaker ECG Quick Reference
PDF Document · PDF