Infective endocarditis clinical symptoms causes and treatment and basic to clinical
infective endocarditis vegetation heart valve clinical findings

This clinical photograph displays a gross anatomical autopsy section of a human heart, focusing on the mitral valve apparatus. The image serves as a pathological specimen demonstrating infective endocarditis. Key findings include the presence of friable, irregular, brownish-red masses, identified as vegetations (indicated by arrows), attached to the mitral valve leaflets. The leaflets themselves show significant structural disruption, ulceration, and non-uniform thickening. Surrounding tissues exhibit signs of acute pathology, including hemorrhagic areas (deep red), inflammatory changes, and yellowish epicardial or valvular fat deposits. The underlying myocardium appears congested. This visual illustrates the hallmark gross features of valvular destruction and vegetation formation in bacterial endocarditis, highlighting the clinical relevance of the condition in causing valvular insufficiency and potential embolic events. The content is suitable for advanced medical education in pathology and cardiology, specifically regarding the morphological manifestations of endocardial infections.

Diagnostic Image: This clinical echocardiogram shows a transthoracic parasternal long-axis view of the heart. Two red arrows and a text label point to 'VEGETATION' located on the aortic valve leaflets. The vegetations appear as small, irregular, and heterogeneous masses with mixed echogenicity (containing both hyperechoic and hypoechoic elements) attached to the valvular structure. These findings are characteristic of infective endocarditis, illustrating the presence of friable microbial growths on the heart valves. The imaging highlights the typical morphology and location of valvular vegetations, which can lead to complications such as aortic regurgitation. This visual is intended for medical education regarding cardiology, infectious diseases, and diagnostic ultrasound interpretation.

Two-panel diagnostic echocardiography (ultrasound) images demonstrating infective endocarditis with multiple vegetations on the left heart valves. The left panel shows a parasternal long-axis view of the bicuspid aortic valve, with hyperechoic, irregular masses (vegetations) attached to the tips of the anterior (yellow arrow) and posterior (blue arrow) leaflets. The right panel highlights the mitral valve apparatus, where another distinct hyperechoic vegetation is visible on the body of the anterior mitral valve leaflet (green arrow). These findings are highly suggestive of active infective endocarditis, illustrating the characteristic appearance of mobile, irregular echoes attached to valvular endocardial surfaces. Clinical relevance includes the high risk of systemic embolization given the size and location of these vegetations, commonly associated with pathogens such as Streptococcus gordonii. This visual serves as an educational tool for identifying valvular pathology, measuring vegetation dimensions, and assessing the risk of secondary complications like septic emboli in a cardiothoracic or infectious disease context.

Two-panel intraoperative clinical photograph (A and B) showing findings during emergency cardiac surgery for infective endocarditis. Panel A illustrates the initial surgical view of the heart with annotations for the right atrium (RA), left atrium (LA), and posterior mitral leaflet (PML). A large, irregular, whitish-tan vegetation is highlighted within a white oval, attached to the mitral valve apparatus. Surgical retractors and a suction vent tube are visible in the field. Panel B shows the surgical site following the partial resection of the PML. The white oval indicates residual vegetation on the mitral annulus. A white arrow points to a pseudoaneurysm located on the posterior wall of the left ventricle (LV), which resulted from a perforated valve annulus abscess. The image demonstrates the destructive nature of bacterial endocarditis on cardiac valves and adjacent myocardial tissue. These frames serve as educational material for cardiac surgeons and residents to understand the intraoperative presentation of complicated mitral valve endocarditis and associated paravalvular complications.
"infective endocarditis"[MeSH Terms] AND treatment
| Type | Pace | Organism | Valve |
|---|---|---|---|
| Acute IE | Days - abrupt, destructive | S. aureus (virulent) | Normal or diseased |
| Subacute IE | Weeks to months - indolent | Viridans streptococci (less virulent) | Pre-existing disease required |
| Organism | Setting | Notes |
|---|---|---|
| Viridans streptococci (S. mitis, S. sanguis, S. mutans) | Community-acquired, SBE | Normal oral flora; damaged valves only |
| S. aureus | Healthcare, IV drug use, ABE | Most common in high-income countries; attacks normal valves |
| Enterococcus faecalis | Genitourinary source; SBE | 5-10% of cases; GI screening recommended |
| S. gallolyticus (bovis) | Elderly, SBE | Associated with colonic neoplasm - colonoscopy mandatory |
| HACEK group | Community, SBE | H aemophilus, A ggregatibacter, C ardiobacterium, E ikenella, K ingella - 5-10% |
| Candida / Aspergillus | IDU, prolonged catheters, TPN, immunosuppression | High mortality; surgery usually required |
| Culture-negative (~10%) | Prior antibiotic use | Difficult-to-isolate organisms (Brucella, Coxiella, Bartonella) |
| Sign | Description | Mechanism |
|---|---|---|
| Petechiae | Small red/brown spots - skin, conjunctiva, oral mucosa | Embolic/vasculitic |
| Splinter hemorrhages | Linear dark streaks under nail beds | Embolic |
| Osler nodes | Painful, tender nodules on pads of fingers/toes | Immune complex vasculitis |
| Janeway lesions | Painless hemorrhagic macules on palms/soles | Septic emboli |
| Roth spots | Oval retinal hemorrhages with pale centers on fundoscopy | Immune complex |
| Clubbing | Chronic, subacute cases | |
| Splenomegaly | More common in subacute IE |
Osler nodes = Painful (O = Ouch); Janeway = Not painful (J = "Just there")



| Organism | Drug | Duration |
|---|---|---|
| Viridans streptococci (PCN-sensitive) | Penicillin G 12-18 MU/day IV or Ceftriaxone 2g/day | 4 weeks (NVE) |
| Viridans streptococci (PCN-resistant) | Penicillin G or Ampicillin + Gentamicin | 4-6 weeks |
| MSSA (Native valve) | Nafcillin/Oxacillin 12g/day IV | 6 weeks |
| MRSA (Native valve) | Vancomycin 15 mg/kg IV q12h OR Daptomycin ≥8 mg/kg/day | 6 weeks |
| MSSA (Prosthetic valve) | Oxacillin + Rifampin 300 mg PO q8h + Gentamicin | ≥6 weeks (gentamicin x 2 wk only) |
| MRSA/MRSE (Prosthetic valve) | Vancomycin + Rifampin + Gentamicin | ≥6 weeks (gentamicin x 2 wk) |
| Enterococcus | Ampicillin + Gentamicin (or Ampicillin + Ceftriaxone for synergy) | 4-6 weeks |
| HACEK organisms | Ceftriaxone 2g/day IV | 4 weeks NVE, 6 weeks PVE |
| Fungal | Amphotericin B + flucytosine; then azole suppression | Valve surgery usually required |
| Culture-negative | Consult Infectious Diseases | Empiric broad-spectrum |
Vancomycin trough goal: 15-20 mcg/mL. Monitor audiometry weekly if aminoglycosides >7 days.
| Complication | Notes |
|---|---|
| Heart failure | Most common cause of death; aortic valve IE has highest risk |
| Perivalvular abscess | S. aureus predominant; presents with new heart block or fever on therapy |
| Embolic stroke | 15-20% of patients; risk highest first 2 weeks |
| Septic emboli | Lungs (right-sided), kidneys, spleen, mesentery |
| Mycotic aneurysm | Intracranial; risk of rupture and hemorrhage |
| Renal failure | From emboli, immune complex nephritis, or drug toxicity |
| Metastatic infection | Vertebral osteomyelitis, psoas abscess, meningitis |
| Relapse | Higher risk with inadequate therapy or IV drug continuation |
| Basic Science Concept | Clinical Correlation |
|---|---|
| NBTE (sterile thrombus on damaged valve) | Predisposing cardiac conditions (MVP, rheumatic, bicuspid AV) |
| Bacterial adherence via surface proteins | Viridans strep after dental work seeds damaged valves |
| Vegetation = fibrin + bacteria + inflammatory cells | Echo shows echogenic, mobile mass |
| Embolization of fragments | Stroke, renal infarct, septic pulmonary emboli |
| Immune complex deposition | Osler nodes, Roth spots, glomerulonephritis, RF positive |
| Perivalvular extension | New heart block on ECG, abscess on TEE, ring abscess at surgery |
| Bacteremia persistence | Daily blood cultures until clearance; fever on therapy = complication |
| Biofilm on prosthetic material | PVE needs rifampin (biofilm penetration) + longer therapy |
Convert to pdf with same detail with drawings and with treatment plan also detail
mkdir -p /tmp/workspace/ie-pdf && ls /tmp/workspace/
~/ie-pdf/generate_ie_pdf.py
#!/usr/bin/env python3
"""
Infective Endocarditis - Comprehensive Medical PDF
Full clinical guide: basic science to clinical management
"""
import urllib.request
import os
from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.units import cm, mm
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_JUSTIFY
from reportlab.platypus import (
SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
HRFlowable, PageBreak, Image, KeepTogether
)
from reportlab.platypus.flowables import Flowable
from reportlab.graphics.shapes import Drawing, Rect, Circle, Line, String, Polygon, Ellipse
from reportlab.graphics import renderPDF
from reportlab.graphics.shapes import ArcPath
import math
# ─────────────────────────────────────────────
# COLORS
# ─────────────────────────────────────────────
DARK_BLUE = colors.HexColor('#1A3A5C')
MED_BLUE = colors.HexColor('#2563A8')
LIGHT_BLUE = colors.HexColor('#D6E8FA')
TEAL = colors.HexColor('#0D6E80')
LIGHT_TEAL = colors.HexColor('#D0F0F4')
RED = colors.HexColor('#C0392B')
LIGHT_RED = colors.HexColor('#FDECEA')
ORANGE = colors.HexColor('#E67E22')
LIGHT_ORANGE= colors.HexColor('#FEF3E2')
GREEN = colors.HexColor('#1E8449')
LIGHT_GREEN = colors.HexColor('#D5F5E3')
YELLOW = colors.HexColor('#F1C40F')
LIGHT_YELLOW= colors.HexColor('#FEFDE6')
PURPLE = colors.HexColor('#6C3483')
LIGHT_PURPLE= colors.HexColor('#F0E6FA')
GRAY_BG = colors.HexColor('#F4F6F9')
GRAY_BORDER = colors.HexColor('#BDC3C7')
WHITE = colors.white
BLACK = colors.black
PAGE_W, PAGE_H = A4
# ─────────────────────────────────────────────
# STYLES
# ─────────────────────────────────────────────
styles = getSampleStyleSheet()
def S(name, **kw):
return ParagraphStyle(name, **kw)
cover_title = S('CoverTitle', fontSize=28, fontName='Helvetica-Bold',
textColor=WHITE, alignment=TA_CENTER, leading=34)
cover_sub = S('CoverSub', fontSize=14, fontName='Helvetica',
textColor=colors.HexColor('#B0D0F0'), alignment=TA_CENTER, leading=18)
cover_author= S('CoverAuthor',fontSize=11, fontName='Helvetica-Oblique',
textColor=colors.HexColor('#90C0E0'), alignment=TA_CENTER)
h1_style = S('H1', fontSize=15, fontName='Helvetica-Bold',
textColor=WHITE, alignment=TA_LEFT, leading=18,
spaceBefore=8, spaceAfter=4,
leftIndent=0, borderPadding=(6,10,6,10))
h2_style = S('H2', fontSize=12, fontName='Helvetica-Bold',
textColor=DARK_BLUE, leading=15,
spaceBefore=8, spaceAfter=3)
h3_style = S('H3', fontSize=10.5, fontName='Helvetica-Bold',
textColor=TEAL, leading=13, spaceBefore=5, spaceAfter=2)
body_style = S('Body', fontSize=9.5, fontName='Helvetica',
textColor=colors.HexColor('#2C3E50'), leading=14,
spaceBefore=2, spaceAfter=2, alignment=TA_JUSTIFY)
bullet_style = S('Bullet', fontSize=9.5, fontName='Helvetica',
textColor=colors.HexColor('#2C3E50'), leading=14,
spaceBefore=1, spaceAfter=1,
leftIndent=14, firstLineIndent=-10)
caption_style = S('Caption', fontSize=8, fontName='Helvetica-Oblique',
textColor=colors.HexColor('#555555'), alignment=TA_CENTER,
spaceBefore=2, spaceAfter=6)
note_style = S('Note', fontSize=8.5, fontName='Helvetica-Oblique',
textColor=colors.HexColor('#444444'), leading=12,
leftIndent=10)
table_header = S('TH', fontSize=9, fontName='Helvetica-Bold',
textColor=WHITE, alignment=TA_CENTER, leading=11)
table_cell = S('TC', fontSize=8.5, fontName='Helvetica',
textColor=BLACK, alignment=TA_LEFT, leading=11)
table_cell_c = S('TCC', fontSize=8.5, fontName='Helvetica',
textColor=BLACK, alignment=TA_CENTER, leading=11)
# ─────────────────────────────────────────────
# HELPER: Section Header Flowable
# ─────────────────────────────────────────────
class SectionHeader(Flowable):
def __init__(self, text, bg_color=DARK_BLUE, text_color=WHITE, width=None, icon=None):
super().__init__()
self.text = text
self.bg_color = bg_color
self.text_color = text_color
self._width = width or (PAGE_W - 4*cm)
self.icon = icon
self.height = 28
def wrap(self, avail_w, avail_h):
return self._width, self.height
def draw(self):
c = self.canv
# Rounded rectangle background
c.setFillColor(self.bg_color)
c.roundRect(0, 0, self._width, self.height, 4, fill=1, stroke=0)
# Left accent bar
c.setFillColor(YELLOW)
c.rect(0, 0, 5, self.height, fill=1, stroke=0)
# Text
c.setFillColor(self.text_color)
c.setFont('Helvetica-Bold', 12)
c.drawString(14, 8, self.text)
class SubHeader(Flowable):
def __init__(self, text, bg_color=LIGHT_BLUE, text_color=DARK_BLUE, width=None):
super().__init__()
self.text = text
self.bg_color = bg_color
self.text_color = text_color
self._width = width or (PAGE_W - 4*cm)
self.height = 22
def wrap(self, avail_w, avail_h):
return self._width, self.height
def draw(self):
c = self.canv
c.setFillColor(self.bg_color)
c.roundRect(0, 0, self._width, self.height, 3, fill=1, stroke=0)
c.setFillColor(MED_BLUE)
c.rect(0, 0, 4, self.height, fill=1, stroke=0)
c.setFillColor(self.text_color)
c.setFont('Helvetica-Bold', 10.5)
c.drawString(12, 6, self.text)
class InfoBox(Flowable):
"""Colored info/warning/note box"""
def __init__(self, lines, color=LIGHT_BLUE, border_color=MED_BLUE, width=None, title=None):
super().__init__()
self.lines = lines
self.color = color
self.border_color = border_color
self._width = width or (PAGE_W - 4*cm)
self.title = title
line_h = 14
self.height = (len(lines) * line_h) + (20 if title else 10) + 10
def wrap(self, avail_w, avail_h):
return self._width, self.height
def draw(self):
c = self.canv
c.setFillColor(self.color)
c.setStrokeColor(self.border_color)
c.setLineWidth(1.2)
c.roundRect(0, 0, self._width, self.height, 4, fill=1, stroke=1)
# Left accent
c.setFillColor(self.border_color)
c.rect(0, 0, 4, self.height, fill=1, stroke=0)
y = self.height - 14
if self.title:
c.setFillColor(self.border_color)
c.setFont('Helvetica-Bold', 9.5)
c.drawString(12, y, self.title)
y -= 16
c.setFillColor(colors.HexColor('#1A1A2E'))
c.setFont('Helvetica', 8.5)
for line in self.lines:
c.drawString(12, y, line)
y -= 14
# ─────────────────────────────────────────────
# DIAGRAM 1: Pathogenesis Flow
# ─────────────────────────────────────────────
class PathogenesisDiagram(Flowable):
def __init__(self, width=None):
super().__init__()
self._width = width or (PAGE_W - 4*cm)
self.height = 130
def wrap(self, avail_w, avail_h):
return self._width, self.height
def draw(self):
c = self.canv
w = self._width
# Background
c.setFillColor(GRAY_BG)
c.roundRect(0, 0, w, self.height, 6, fill=1, stroke=0)
boxes = [
("Endothelial\nDamage", MED_BLUE, WHITE),
("Sterile\nThrombus\n(NBTE)", TEAL, WHITE),
("Bacteremia\nSeeding", ORANGE, WHITE),
("Vegetation\nFormation", RED, WHITE),
("Systemic\nComplications", PURPLE, WHITE),
]
n = len(boxes)
box_w = 70
gap = (w - n * box_w) / (n + 1)
box_h = 60
y_box = (self.height - box_h) / 2
for i, (label, bg, fg) in enumerate(boxes):
x = gap + i * (box_w + gap)
# Box
c.setFillColor(bg)
c.roundRect(x, y_box, box_w, box_h, 5, fill=1, stroke=0)
# Text
c.setFillColor(fg)
c.setFont('Helvetica-Bold', 7.5)
lines = label.split('\n')
line_h = 10
total_h = len(lines) * line_h
y_text = y_box + box_h/2 + total_h/2 - line_h
for line in lines:
c.drawCentredString(x + box_w/2, y_text, line)
y_text -= line_h
# Number circle
c.setFillColor(YELLOW)
c.circle(x + box_w/2, y_box + box_h + 8, 8, fill=1, stroke=0)
c.setFillColor(DARK_BLUE)
c.setFont('Helvetica-Bold', 8)
c.drawCentredString(x + box_w/2, y_box + box_h + 4.5, str(i+1))
# Arrow
if i < n - 1:
ax = x + box_w + 4
ay = y_box + box_h/2
c.setStrokeColor(DARK_BLUE)
c.setFillColor(DARK_BLUE)
c.setLineWidth(1.5)
c.line(ax, ay, ax + gap - 8, ay)
# Arrowhead
c.setFillColor(DARK_BLUE)
p = c.beginPath()
p.moveTo(ax + gap - 8, ay)
p.lineTo(ax + gap - 14, ay + 4)
p.lineTo(ax + gap - 14, ay - 4)
p.close()
c.drawPath(p, fill=1, stroke=0)
# Title
c.setFillColor(DARK_BLUE)
c.setFont('Helvetica-Bold', 9)
c.drawCentredString(w/2, self.height - 12, "PATHOGENESIS OF INFECTIVE ENDOCARDITIS")
# ─────────────────────────────────────────────
# DIAGRAM 2: Heart with IE sites
# ─────────────────────────────────────────────
class HeartDiagram(Flowable):
def __init__(self, width=None):
super().__init__()
self._width = width or 260
self.height = 220
def wrap(self, avail_w, avail_h):
return self._width, self.height
def draw(self):
c = self.canv
w, h = self._width, self.height
cx, cy = w/2, h/2 - 10
# Background
c.setFillColor(GRAY_BG)
c.roundRect(0, 0, w, h, 6, fill=1, stroke=0)
c.setFillColor(DARK_BLUE)
c.setFont('Helvetica-Bold', 9)
c.drawCentredString(w/2, h - 14, "HEART VALVES AFFECTED IN IE")
# Heart outline (simplified)
c.setFillColor(colors.HexColor('#FFB3B3'))
c.setStrokeColor(RED)
c.setLineWidth(1.5)
# Left ventricle
p = c.beginPath()
p.moveTo(cx - 30, cy)
p.curveTo(cx - 55, cy + 30, cx - 55, cy + 60, cx, cy + 80)
p.curveTo(cx + 55, cy + 60, cx + 55, cy + 30, cx + 30, cy)
p.lineTo(cx - 30, cy)
c.drawPath(p, fill=1, stroke=1)
# Atria
c.setFillColor(colors.HexColor('#FFD0D0'))
c.ellipse(cx - 50, cy + 10, cx - 10, cy + 50, fill=1, stroke=1) # RA
c.ellipse(cx + 10, cy + 10, cx + 50, cy + 50, fill=1, stroke=1) # LA
# Valve positions and labels
valves = [
(cx, cy + 28, "Mitral\nValve", MED_BLUE, 1),
(cx, cy + 5, "Aortic\nValve", RED, 2),
(cx - 30, cy + 28, "Tricuspid\nValve", TEAL, 3),
(cx + 30, cy + 28, "Pulmonary\nValve", GREEN, 4),
]
for vx, vy, label, clr, num in valves:
c.setFillColor(clr)
c.circle(vx, vy, 7, fill=1, stroke=0)
c.setFillColor(WHITE)
c.setFont('Helvetica-Bold', 7)
c.drawCentredString(vx, vy - 2.5, str(num))
# Legend
legend_items = [
("1 Mitral (most common - left-sided)", MED_BLUE),
("2 Aortic (worst prognosis)", RED),
("3 Tricuspid (IV drug users)", TEAL),
("4 Pulmonary (rare)", GREEN),
]
lx, ly = 5, 68
c.setFont('Helvetica-Bold', 7.5)
c.setFillColor(DARK_BLUE)
c.drawString(lx, ly + 4, "VALVE INVOLVEMENT:")
ly -= 2
for text, clr in legend_items:
ly -= 14
c.setFillColor(clr)
c.circle(lx + 5, ly + 4, 5, fill=1, stroke=0)
c.setFillColor(BLACK)
c.setFont('Helvetica', 7.5)
c.drawString(lx + 14, ly, text)
# ─────────────────────────────────────────────
# DIAGRAM 3: Clinical Features Wheel
# ─────────────────────────────────────────────
class ClinicalFeaturesDiagram(Flowable):
def __init__(self, width=None):
super().__init__()
self._width = width or (PAGE_W - 4*cm)
self.height = 160
def wrap(self, avail_w, avail_h):
return self._width, self.height
def draw(self):
c = self.canv
w = self._width
# Background
c.setFillColor(GRAY_BG)
c.roundRect(0, 0, w, self.height, 6, fill=1, stroke=0)
# Title
c.setFillColor(DARK_BLUE)
c.setFont('Helvetica-Bold', 9)
c.drawCentredString(w/2, self.height - 12, "CLINICAL FEATURES AT A GLANCE")
categories = [
("CONSTITUTIONAL\n(>90%)", ["Fever (most consistent)", "Chills/Rigors", "Night sweats", "Weight loss", "Fatigue, malaise"], MED_BLUE, LIGHT_BLUE),
("CARDIAC\nSIGNS", ["New/changing murmur", "Heart failure", "Pericardial rub", "Conduction block", "(from perivalvular\nabscess)"], TEAL, LIGHT_TEAL),
("PERIPHERAL\nSTIGMATA", ["Petechiae", "Splinter hemorrhages", "Osler nodes (painful)", "Janeway lesions (painless)", "Roth spots (fundus)", "Clubbing"], ORANGE, LIGHT_ORANGE),
("EMBOLIC\nEVENTS", ["Stroke/TIA (15-20%)", "Renal infarct", "Splenic infarct", "Septic pulm emboli\n(right-sided IE)", "Mycotic aneurysm"], RED, LIGHT_RED),
]
n = len(categories)
box_w = (w - 10) / n - 5
box_h = 120
y_start = 20
for i, (title, items, fg, bg) in enumerate(categories):
x = 5 + i * (box_w + 5)
# Card background
c.setFillColor(bg)
c.setStrokeColor(fg)
c.setLineWidth(1)
c.roundRect(x, y_start, box_w, box_h, 4, fill=1, stroke=1)
# Header
c.setFillColor(fg)
c.roundRect(x, y_start + box_h - 30, box_w, 30, 4, fill=1, stroke=0)
c.setFillColor(fg)
c.rect(x, y_start + box_h - 30, box_w, 15, fill=1, stroke=0) # flat bottom
c.setFillColor(WHITE)
c.setFont('Helvetica-Bold', 7)
title_lines = title.split('\n')
ty = y_start + box_h - 14
for tl in title_lines:
c.drawCentredString(x + box_w/2, ty, tl)
ty -= 10
# Items
c.setFillColor(colors.HexColor('#1A1A2E'))
c.setFont('Helvetica', 7)
iy = y_start + box_h - 36
for item in items:
if iy < y_start + 4:
break
lines = item.split('\n')
for ln in lines:
c.drawString(x + 8, iy, '• ' + ln if ln == lines[0] else ' ' + ln)
iy -= 11
if iy < y_start + 4:
break
# ─────────────────────────────────────────────
# DIAGRAM 4: Treatment Algorithm Flowchart
# ─────────────────────────────────────────────
class TreatmentFlowchart(Flowable):
def __init__(self, width=None):
super().__init__()
self._width = width or (PAGE_W - 4*cm)
self.height = 320
def wrap(self, avail_w, avail_h):
return self._width, self.height
def draw(self):
c = self.canv
w = self._width
h = self.height
c.setFillColor(GRAY_BG)
c.roundRect(0, 0, w, h, 6, fill=1, stroke=0)
c.setFillColor(DARK_BLUE)
c.setFont('Helvetica-Bold', 9)
c.drawCentredString(w/2, h - 14, "TREATMENT DECISION ALGORITHM")
def box(x, y, bw, bh, text, bg, fg=WHITE, font_size=8):
c.setFillColor(bg)
c.setStrokeColor(colors.HexColor('#888888'))
c.setLineWidth(0.5)
c.roundRect(x, y, bw, bh, 4, fill=1, stroke=1)
c.setFillColor(fg)
c.setFont('Helvetica-Bold', font_size)
lines = text.split('\n')
total = len(lines) * (font_size + 2)
ty = y + bh/2 + total/2 - (font_size + 2)
for ln in lines:
c.drawCentredString(x + bw/2, ty, ln)
ty -= (font_size + 2)
def diamond(x, y, bw, bh, text, bg=YELLOW, fg=DARK_BLUE):
cx_ = x + bw/2
cy_ = y + bh/2
c.setFillColor(bg)
c.setStrokeColor(DARK_BLUE)
c.setLineWidth(0.8)
p = c.beginPath()
p.moveTo(cx_, y + bh)
p.lineTo(x + bw, cy_)
p.lineTo(cx_, y)
p.lineTo(x, cy_)
p.close()
c.drawPath(p, fill=1, stroke=1)
c.setFillColor(fg)
c.setFont('Helvetica-Bold', 7.5)
lines = text.split('\n')
ty = cy_ + len(lines) * 4.5 - 4
for ln in lines:
c.drawCentredString(cx_, ty, ln)
ty -= 10
def arrow(x1, y1, x2, y2):
c.setStrokeColor(DARK_BLUE)
c.setFillColor(DARK_BLUE)
c.setLineWidth(1.2)
c.line(x1, y1, x2, y2)
# arrowhead at end
angle = math.atan2(y2-y1, x2-x1)
ah = 7
aw = 4
c.setFillColor(DARK_BLUE)
p = c.beginPath()
p.moveTo(x2, y2)
p.lineTo(x2 - ah*math.cos(angle) + aw*math.sin(angle),
y2 - ah*math.sin(angle) - aw*math.cos(angle))
p.lineTo(x2 - ah*math.cos(angle) - aw*math.sin(angle),
y2 - ah*math.sin(angle) + aw*math.cos(angle))
p.close()
c.drawPath(p, fill=1, stroke=0)
def label(x, y, text, clr=DARK_BLUE):
c.setFillColor(clr)
c.setFont('Helvetica-Bold', 7.5)
c.drawString(x, y, text)
# ---- LAYOUT ----
top_y = h - 35
bw_main = 160
bx = (w - bw_main) / 2
# Step 1
box(bx, top_y - 30, bw_main, 26, "Suspected IE\n(Fever + Murmur / Risk Factors)", MED_BLUE)
arrow(bx + bw_main/2, top_y - 30, bx + bw_main/2, top_y - 65)
# Step 2
box(bx, top_y - 91, bw_main, 26, "Obtain 3 Blood Cultures\n(separate sites, 30 min apart)", TEAL)
arrow(bx + bw_main/2, top_y - 91, bx + bw_main/2, top_y - 126)
# Step 3
box(bx, top_y - 152, bw_main, 26, "Echocardiography\n(TTE first; TEE if negative)", TEAL)
arrow(bx + bw_main/2, top_y - 152, bx + bw_main/2, top_y - 187)
# Diamond: Duke Criteria
diamond(bx + 10, top_y - 225, bw_main - 20, 36, "Modified Duke\nCriteria Met?")
arrow(bx + bw_main/2, top_y - 225, bx + bw_main/2, top_y - 260)
# Yes - Start antibiotics
box(bx, top_y - 286, bw_main, 26, "Start IV Antibiotics\n(organism-directed, 4-6 wks)", GREEN)
# No branch
no_x = bx + bw_main + 15
c.setStrokeColor(DARK_BLUE)
c.setLineWidth(1.2)
c.line(bx + bw_main - 10, top_y - 207, no_x + 70, top_y - 207)
arrow(no_x + 70, top_y - 207, no_x + 70, top_y - 225)
box(no_x, top_y - 251, 85, 26, "Possible IE -\nRepeat cultures\n& imaging", ORANGE, font_size=7)
label(bx + bw_main + 2, top_y - 210, "NO", RED)
label(bx + bw_main/2 - 12, top_y - 255, "YES", GREEN)
# Surgery decision
surgery_y = top_y - 315
c.setStrokeColor(DARK_BLUE)
c.setLineWidth(1.2)
c.line(bx + bw_main/2, top_y - 286, bx + bw_main/2, surgery_y + 60)
# Two branches at bottom
surg_x1 = 5
surg_x2 = w/2 + 10
surg_bw = w/2 - 20
c.line(bx + bw_main/2, surgery_y + 60, surg_x1 + surg_bw/2, surgery_y + 60)
c.line(bx + bw_main/2, surgery_y + 60, surg_x2 + surg_bw/2, surgery_y + 60)
arrow(surg_x1 + surg_bw/2, surgery_y + 60, surg_x1 + surg_bw/2, surgery_y + 36)
arrow(surg_x2 + surg_bw/2, surgery_y + 60, surg_x2 + surg_bw/2, surgery_y + 36)
box(surg_x1, surgery_y, surg_bw, 36,
"MEDICAL Rx\nOnly\n(4-6 wks IV abx)", GREEN, font_size=7.5)
box(surg_x2, surgery_y, surg_bw, 36,
"SURGERY +\nMedical Rx\n(Valve repair/replace)", RED, font_size=7.5)
label(surg_x1 + surg_bw/2 - 18, surgery_y + 40, "No surgery\nindications", colors.HexColor('#1A6620'))
label(surg_x2 + surg_bw/2 - 20, surgery_y + 40, "Surgery\nindicated", RED)
# ─────────────────────────────────────────────
# DIAGRAM 5: Duke Criteria Visual
# ─────────────────────────────────────────────
class DukeCriteriaDiagram(Flowable):
def __init__(self, width=None):
super().__init__()
self._width = width or (PAGE_W - 4*cm)
self.height = 155
def wrap(self, avail_w, avail_h):
return self._width, self.height
def draw(self):
c = self.canv
w = self._width
c.setFillColor(GRAY_BG)
c.roundRect(0, 0, w, self.height, 6, fill=1, stroke=0)
c.setFillColor(DARK_BLUE)
c.setFont('Helvetica-Bold', 9)
c.drawCentredString(w/2, self.height - 12, "MODIFIED DUKE CRITERIA - VISUAL SUMMARY")
# Major criteria box
mc_x, mc_y = 5, 20
mc_w = w*0.44
mc_h = 110
c.setFillColor(LIGHT_BLUE)
c.setStrokeColor(MED_BLUE)
c.setLineWidth(1.2)
c.roundRect(mc_x, mc_y, mc_w, mc_h, 4, fill=1, stroke=1)
c.setFillColor(MED_BLUE)
c.roundRect(mc_x, mc_y + mc_h - 20, mc_w, 20, 4, fill=1, stroke=0)
c.rect(mc_x, mc_y + mc_h - 20, mc_w, 10, fill=1, stroke=0)
c.setFillColor(WHITE)
c.setFont('Helvetica-Bold', 8.5)
c.drawCentredString(mc_x + mc_w/2, mc_y + mc_h - 14, "MAJOR CRITERIA (2 needed)")
c.setFillColor(BLACK)
c.setFont('Helvetica', 7.5)
major = [
"1. Positive blood cultures:",
" - Typical organism x2 separate cultures",
" - Viridans strep, S. bovis, HACEK,",
" S. aureus, Enterococcus (no primary focus)",
" - Persistently positive cultures >12h apart",
"2. Endocardial involvement:",
" - Vegetation/abscess/new regurgitation on echo",
" - Positive 18F-FDG PET/CT (updated criteria)",
]
ty = mc_y + mc_h - 28
for line in major:
c.drawString(mc_x + 6, ty, line)
ty -= 10
# Minor criteria box
mn_x = mc_x + mc_w + 10
mn_w = w - mn_x - 5
c.setFillColor(LIGHT_TEAL)
c.setStrokeColor(TEAL)
c.roundRect(mn_x, mc_y, mn_w, mc_h, 4, fill=1, stroke=1)
c.setFillColor(TEAL)
c.roundRect(mn_x, mc_y + mc_h - 20, mn_w, 20, 4, fill=1, stroke=0)
c.rect(mn_x, mc_y + mc_h - 20, mn_w, 10, fill=1, stroke=0)
c.setFillColor(WHITE)
c.setFont('Helvetica-Bold', 8.5)
c.drawCentredString(mn_x + mn_w/2, mc_y + mc_h - 14, "MINOR CRITERIA (3-5 needed)")
c.setFillColor(BLACK)
c.setFont('Helvetica', 7.5)
minor = [
"1. Predisposing valve/heart condition or IDU",
"2. Fever >38°C",
"3. Vascular phenomena:",
" (emboli, Janeway lesions, mycotic aneurysm,",
" intracranial hemorrhage)",
"4. Immunologic phenomena:",
" (Osler nodes, Roth spots, RF positive, GN)",
"5. Microbiologic: not meeting major criteria",
]
ty = mc_y + mc_h - 28
for line in minor:
c.drawString(mn_x + 6, ty, line)
ty -= 10
# Scoring legend
score_items = [
("DEFINITE: 2 Major OR 1 Major + 3 Minor OR 5 Minor", GREEN),
("POSSIBLE: 1 Major + 1 Minor OR 3 Minor", ORANGE),
("REJECTED: Alternative diagnosis / Symptoms resolve ≤4d on abx", RED),
]
c.setFont('Helvetica-Bold', 7.5)
sx = 5
sy = 16
for text, clr in score_items:
c.setFillColor(clr)
c.rect(sx, sy - 1, 8, 8, fill=1, stroke=0)
c.setFillColor(BLACK)
c.setFont('Helvetica', 7)
c.drawString(sx + 12, sy, text)
sx += (w) // 3
# ─────────────────────────────────────────────
# DIAGRAM 6: Complication Map
# ─────────────────────────────────────────────
class ComplicationDiagram(Flowable):
def __init__(self, width=None):
super().__init__()
self._width = width or (PAGE_W - 4*cm)
self.height = 145
def wrap(self, avail_w, avail_h):
return self._width, self.height
def draw(self):
c = self.canv
w = self._width
c.setFillColor(GRAY_BG)
c.roundRect(0, 0, w, self.height, 6, fill=1, stroke=0)
c.setFillColor(DARK_BLUE)
c.setFont('Helvetica-Bold', 9)
c.drawCentredString(w/2, self.height - 12, "COMPLICATIONS MAP")
# Center node
cx, cy = w/2, self.height/2 - 8
c.setFillColor(RED)
c.circle(cx, cy, 28, fill=1, stroke=0)
c.setFillColor(WHITE)
c.setFont('Helvetica-Bold', 8)
c.drawCentredString(cx, cy + 4, "INFECTIVE")
c.drawCentredString(cx, cy - 6, "ENDOCARDITIS")
complications = [
(cx - 100, cy + 60, "Heart\nFailure\n(#1 cause death)", RED),
(cx + 100, cy + 60, "Perivalvular\nAbscess\n(heart block)", PURPLE),
(cx - 120, cy, "Embolic\nStroke\n(15-20%)", MED_BLUE),
(cx + 120, cy, "Septic\nEmboli\n(lungs, spleen)", TEAL),
(cx - 100, cy - 60, "Mycotic\nAneurysm\n(rupture risk)", ORANGE),
(cx + 100, cy - 60, "Renal\nFailure\n(GN / emboli)", colors.HexColor('#795548')),
]
c.setFont('Helvetica-Bold', 7)
for ex, ey, label, clr in complications:
# Draw spoke
c.setStrokeColor(clr)
c.setLineWidth(1.2)
dx = cx - ex; dy = cy - ey
dist = (dx**2 + dy**2)**0.5
ratio = 30 / dist
c.line(cx - dx*ratio, cy - dy*ratio, ex + dx*0.28, ey + dy*0.28)
# Node
bw, bh = 70, 34
c.setFillColor(clr)
c.roundRect(ex - bw/2, ey - bh/2, bw, bh, 4, fill=1, stroke=0)
c.setFillColor(WHITE)
lines = label.split('\n')
ty2 = ey + len(lines)*4
for ln in lines:
c.drawCentredString(ex, ty2, ln)
ty2 -= 10
# ─────────────────────────────────────────────
# DOWNLOAD CLINICAL IMAGES
# ─────────────────────────────────────────────
def download_image(url, path):
try:
req = urllib.request.Request(url, headers={'User-Agent': 'Mozilla/5.0'})
with urllib.request.urlopen(req, timeout=15) as resp:
with open(path, 'wb') as f:
f.write(resp.read())
return True
except Exception as e:
print(f" Image download failed {url}: {e}")
return False
img_dir = '/tmp/workspace/ie-pdf/imgs'
os.makedirs(img_dir, exist_ok=True)
images = {
'petechiae': ('https://cdn.orris.care/cdss_images/08c7790da4a166e7f1057c3e50834a12f2b268dbe140f0c1937b611b13bc9287.png',
'Petechiae in infective endocarditis (Goldman-Cecil Medicine)'),
'osler': ('https://cdn.orris.care/cdss_images/d68331a17f9f5dfd50561313ffefe6ad679d76490677de2a87a298fb744fc968.png',
'Osler node on finger in infective endocarditis (Goldman-Cecil Medicine)'),
'echo_aortic':('https://cdn.orris.care/cdss_images/pmc_clinical_VQA_21611e4e2b613f3e8db5e51a70368da53410ac51ae91ccc38186cae1fbed7aa4.jpg',
'Transthoracic echocardiogram: vegetation on aortic valve (arrows)'),
'gross_valve':('https://cdn.orris.care/cdss_images/pmc_clinical_VQA_f0f645951ff159780dab62d6de772480be7275751768b2e765c9318e166dffab.jpg',
'Gross pathology: mitral valve vegetations (friable, arrows) with leaflet destruction'),
}
print("Downloading clinical images...")
local_imgs = {}
for key, (url, cap) in images.items():
path = os.path.join(img_dir, f'{key}.jpg' if url.endswith('.jpg') else f'{key}.png')
if download_image(url, path):
local_imgs[key] = (path, cap)
print(f" OK: {key}")
else:
local_imgs[key] = None
# ─────────────────────────────────────────────
# TABLE HELPERS
# ─────────────────────────────────────────────
def make_table(data, col_widths, header_bg=DARK_BLUE, alt_row=GRAY_BG, font_size=8.5):
style = [
('BACKGROUND', (0,0), (-1,0), header_bg),
('TEXTCOLOR', (0,0), (-1,0), WHITE),
('FONTNAME', (0,0), (-1,0), 'Helvetica-Bold'),
('FONTSIZE', (0,0), (-1,0), font_size),
('ALIGN', (0,0), (-1,0), 'CENTER'),
('VALIGN', (0,0), (-1,-1),'MIDDLE'),
('FONTNAME', (0,1), (-1,-1),'Helvetica'),
('FONTSIZE', (0,1), (-1,-1), font_size - 0.5),
('ROWBACKGROUND', (0,1), (-1,-1), [WHITE, alt_row]),
('GRID', (0,0), (-1,-1), 0.4, GRAY_BORDER),
('TOPPADDING', (0,0), (-1,-1), 4),
('BOTTOMPADDING',(0,0),(-1,-1), 4),
('LEFTPADDING', (0,0),(-1,-1), 5),
('RIGHTPADDING',(0,0),(-1,-1), 5),
]
t = Table(data, colWidths=col_widths)
t.setStyle(TableStyle(style))
return t
def P(text, style=body_style):
return Paragraph(text, style)
def B(text):
return Paragraph('• ' + text, bullet_style)
def SP(h=0.2):
return Spacer(1, h*cm)
# ─────────────────────────────────────────────
# BUILD PDF
# ─────────────────────────────────────────────
out_path = '/tmp/workspace/ie-pdf/Infective_Endocarditis_Complete_Guide.pdf'
doc = SimpleDocTemplate(
out_path,
pagesize=A4,
rightMargin=2*cm, leftMargin=2*cm,
topMargin=2*cm, bottomMargin=2*cm,
title='Infective Endocarditis: Complete Clinical Guide',
author='Orris Medical AI',
subject='Basic to Clinical: IE Comprehensive Reference'
)
story = []
# ═══════════════════════════════════════════════════════
# COVER PAGE
# ═══════════════════════════════════════════════════════
class CoverPage(Flowable):
def wrap(self, aw, ah):
return aw, ah
def draw(self):
c = self.canv
pw, ph = PAGE_W, PAGE_H
# Background gradient effect
for i in range(20):
frac = i / 20
r = 0.10 + frac * 0.05
g = 0.23 + frac * 0.08
b = 0.36 + frac * 0.15
c.setFillColorRGB(r, g, b)
c.rect(0, ph * (1 - (i+1)/20), pw, ph/20, fill=1, stroke=0)
# Decorative circles
c.setFillColor(colors.HexColor('#1E4A70'))
c.circle(pw*0.85, ph*0.15, 80, fill=1, stroke=0)
c.setFillColor(colors.HexColor('#163A5C'))
c.circle(pw*0.1, ph*0.8, 60, fill=1, stroke=0)
c.setFillColor(colors.HexColor('#0E2840'))
c.circle(pw*0.75, ph*0.7, 40, fill=1, stroke=0)
# Accent bar
c.setFillColor(YELLOW)
c.rect(0, ph*0.52, pw, 5, fill=1, stroke=0)
c.setFillColor(colors.HexColor('#E67E22'))
c.rect(0, ph*0.50, pw, 3, fill=1, stroke=0)
# Main Title
c.setFillColor(WHITE)
c.setFont('Helvetica-Bold', 36)
c.drawCentredString(pw/2, ph*0.68, "INFECTIVE ENDOCARDITIS")
c.setFont('Helvetica-Bold', 18)
c.setFillColor(colors.HexColor('#B0D8F8'))
c.drawCentredString(pw/2, ph*0.60, "Complete Clinical Reference")
c.setFont('Helvetica', 13)
c.setFillColor(colors.HexColor('#90C4E8'))
c.drawCentredString(pw/2, ph*0.55, "Basic Science · Clinical Presentation · Diagnosis · Treatment")
# Bottom info block
c.setFillColor(colors.HexColor('#0D1F30'))
c.rect(0, 0, pw, ph*0.38, fill=1, stroke=0)
topics = [
("Pathogenesis", "Microbiology", "Risk Factors"),
("Clinical Signs", "Duke Criteria", "Echocardiography"),
("Antibiotic Therapy", "Surgical Indications", "Prophylaxis"),
]
c.setFillColor(WHITE)
c.setFont('Helvetica-Bold', 11)
c.drawCentredString(pw/2, ph*0.36, "TOPICS COVERED")
c.setFillColor(GRAY_BORDER)
c.rect(pw*0.15, ph*0.346, pw*0.70, 0.5, fill=1, stroke=0)
c.setFont('Helvetica', 9.5)
c.setFillColor(colors.HexColor('#A0C4E0'))
start_y = ph*0.32
for row in topics:
start_y -= 0.018*ph
for j, topic in enumerate(row):
x = pw * (0.2 + j * 0.22)
c.setFillColor(MED_BLUE)
c.circle(x - 8, start_y + 3, 3, fill=1, stroke=0)
c.setFillColor(colors.HexColor('#C8E0F0'))
c.drawString(x - 2, start_y, topic)
# Source tag
c.setFillColor(colors.HexColor('#607080'))
c.setFont('Helvetica-Oblique', 8)
c.drawCentredString(pw/2, ph*0.04, 'Sources: Robbins Pathology · Braunwald\'s Heart Disease · Goldman-Cecil Medicine · Washington Manual · Rosen\'s Emergency Medicine')
c.drawCentredString(pw/2, ph*0.025, 'Generated by Orris Medical AI · August 2026')
story.append(CoverPage())
story.append(PageBreak())
# ═══════════════════════════════════════════════════════
# SECTION 1: DEFINITION & CLASSIFICATION
# ═══════════════════════════════════════════════════════
story.append(SectionHeader("1. DEFINITION & CLASSIFICATION"))
story.append(SP(0.3))
story.append(P(
"<b>Infective endocarditis (IE)</b> is a microbial infection of the endocardial surface of the heart, "
"primarily affecting cardiac valves. It leads to the formation of <b>vegetations</b> - masses composed of "
"thrombotic debris, inflammatory cells, and organisms - often with destruction of the underlying cardiac tissue. "
"Prosthetic valves, intracardiac devices (pacemakers, ICDs), aneurysmal sacs, and other vascular structures may also be involved."
))
story.append(SP(0.3))
class_data = [
[P("Feature", table_header), P("Acute IE", table_header), P("Subacute IE", table_header)],
[P("Onset", table_cell_c), P("Days - abrupt, rapidly progressive", table_cell), P("Weeks to months - insidious", table_cell)],
[P("Typical Organism", table_cell_c), P("S. aureus (high virulence)", table_cell), P("Viridans streptococci (low virulence)", table_cell)],
[P("Valve Requirement", table_cell_c), P("Can infect normal valves", table_cell), P("Requires pre-existing valve disease", table_cell)],
[P("Destruction", table_cell_c), P("Severe, rapid valve destruction", table_cell), P("Slower, less destructive", table_cell)],
[P("Morbidity/Mortality", table_cell_c), P("High even with therapy", table_cell), P("Most recover with appropriate antibiotics", table_cell)],
[P("Immune Phenomena", table_cell_c), P("Less prominent", table_cell), P("More prominent (Osler nodes, RF, GN)", table_cell)],
]
cw = (PAGE_W - 4*cm) / 8
story.append(make_table(class_data, [cw*2, cw*3, cw*3]))
story.append(SP(0.2))
story.append(P('<i>Note: Many cases fall between acute and subacute - classification by causative organism, valve involved, and pace of illness is preferred.</i>', note_style))
story.append(SP(0.4))
# ═══════════════════════════════════════════════════════
# SECTION 2: EPIDEMIOLOGY
# ═══════════════════════════════════════════════════════
story.append(SectionHeader("2. EPIDEMIOLOGY"))
story.append(SP(0.3))
epi_data = [
["Incidence", "~9-15 cases/100,000/year; 10,000-20,000 new US cases/year"],
["90-day Mortality", "~25% (worse with S. aureus, heart failure, perivalvular extension)"],
["Age Trend", "Increasingly a disease of older adults (>60 years)"],
["Healthcare-Associated", "Up to 36% of community-onset IE; linked to catheters, dialysis, procedures"],
["IV Drug Use", "7% (2000) → 12%+ (2013); >50% of cases in some communities (opioid epidemic)"],
["Prosthetic Valve IE", "Complicates up to 6% of valves; highest risk first 6 months post-surgery"],
["Right-Sided IE", "Tricuspid valve in ~50% of IDU cases; better prognosis than left-sided"],
]
cw2 = (PAGE_W - 4*cm) / 5
t2 = [[P(r[0], ParagraphStyle('TH2', fontSize=9, fontName='Helvetica-Bold', textColor=DARK_BLUE)),
P(r[1], body_style)] for r in epi_data]
t2_tbl = Table(t2, colWidths=[cw2*1.5, cw2*3.5])
t2_tbl.setStyle(TableStyle([
('ROWBACKGROUNDS', (0,0), (-1,-1), [WHITE, GRAY_BG]),
('GRID', (0,0), (-1,-1), 0.4, GRAY_BORDER),
('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
('TOPPADDING', (0,0), (-1,-1), 4),
('BOTTOMPADDING', (0,0), (-1,-1), 4),
('LEFTPADDING', (0,0), (-1,-1), 5),
]))
story.append(t2_tbl)
story.append(SP(0.4))
# ═══════════════════════════════════════════════════════
# SECTION 3: PATHOGENESIS
# ═══════════════════════════════════════════════════════
story.append(SectionHeader("3. PATHOGENESIS (Basic Science)"))
story.append(SP(0.3))
story.append(PathogenesisDiagram())
story.append(SP(0.3))
patho_steps = [
("<b>Step 1 - Endothelial/Valve Damage:</b> Turbulent blood flow from valvular disease (MVP, bicuspid AV, rheumatic), "
"prosthetic material, jet lesions from congenital shunts, or catheter trauma disrupts the valve endothelium, "
"exposing subendothelial collagen."),
("<b>Step 2 - Sterile Thrombus (NBTE - Non-Bacterial Thrombotic Endocarditis):</b> Platelets and fibrin deposit "
"on the exposed collagen, forming a sterile platelet-fibrin thrombus. This is the nidus for bacterial seeding."),
("<b>Step 3 - Bacteremia:</b> Microorganisms enter the bloodstream (dental procedures, skin infections, IV drug use, "
"IV catheters, urogenital instrumentation, occult gut/oral flora). Even trivial activities can cause transient bacteremia."),
("<b>Step 4 - Vegetation Formation:</b> Bacteria adhere to the sterile thrombus via surface adhesins. They proliferate "
"within the fibrin matrix, forming a <b>vegetation</b> - a layered structure of fibrin, platelets, bacteria, and "
"inflammatory cells. Bacteria deep in the vegetation are protected from antibiotics and phagocytes, requiring prolonged therapy."),
("<b>Step 5 - Systemic Complications:</b> Vegetations can (a) embolize distally causing infarcts in the brain, "
"kidneys, spleen, and lungs; (b) extend locally causing perivalvular abscesses, fistulas, and conduction defects; "
"(c) trigger immune complex deposition causing nephritis, Osler nodes, and Roth spots."),
]
for step in patho_steps:
story.append(B(step))
story.append(SP(0.4))
# ═══════════════════════════════════════════════════════
# SECTION 4: CAUSES & MICROBIOLOGY
# ═══════════════════════════════════════════════════════
story.append(SectionHeader("4. CAUSES & MICROBIOLOGY"))
story.append(SP(0.3))
story.append(SubHeader("4a. Native Valve IE (NVE)"))
story.append(SP(0.2))
micro_data = [
[P("Organism", table_header), P("Clinical Setting", table_header), P("Course", table_header), P("Special Notes", table_header)],
[P("Viridans Streptococci\n(S. mitis, S. sanguis, S. mutans)", table_cell),
P("Community-acquired; oral flora after dental work", table_cell),
P("Subacute", table_cell),
P("Most common in developing countries; requires damaged valve", table_cell)],
[P("S. aureus", table_cell),
P("Healthcare-associated; IV drug users; skin infections", table_cell),
P("Acute", table_cell),
P("#1 cause in high-income countries; attacks normal valves; rapid destruction", table_cell)],
[P("Enterococcus faecalis", table_cell),
P("Community + nosocomial; GU source", table_cell),
P("Subacute", table_cell),
P("5-10% of cases; associated with GI neoplasm - screen colon", table_cell)],
[P("S. gallolyticus (bovis)", table_cell),
P("Elderly; community", table_cell),
P("Subacute", table_cell),
P("MANDATORY colonoscopy - strong association with colon cancer", table_cell)],
[P("HACEK group\n(H/A/C/E/K)", table_cell),
P("Community; oral cavity commensals", table_cell),
P("Subacute", table_cell),
P("5-10% of community IE; grow within 7 days in modern blood cultures", table_cell)],
[P("Coagulase-negative Staph\n(S. epidermidis, S. lugdunensis)", table_cell),
P("Prosthetic valves; healthcare", table_cell),
P("Subacute", table_cell),
P("S. lugdunensis: aggressive like S. aureus; high perivalvular extension", table_cell)],
[P("Candida / Aspergillus", table_cell),
P("IDU; TPN; prolonged catheters; immunosuppressed", table_cell),
P("Subacute/chronic", table_cell),
P("Very high mortality; Aspergillus rarely grows in blood cultures; surgery usually required", table_cell)],
[P("Culture-negative (~10%)", table_cell),
P("Prior antibiotics; Coxiella, Bartonella, Brucella", table_cell),
P("Variable", table_cell),
P("ID consultation essential; serologic testing needed", table_cell)],
]
cw3 = (PAGE_W - 4*cm) / 14
story.append(make_table(micro_data, [cw3*3, cw3*3, cw3*2, cw3*6]))
story.append(SP(0.3))
story.append(SubHeader("4b. Prosthetic Valve IE (PVE)"))
story.append(SP(0.2))
pve_data = [
[P("Timing", table_header), P("Definition", table_header), P("Common Organisms", table_header), P("Prognosis", table_header)],
[P("Early PVE", table_cell_c), P("Within 2 months of surgery\n(perioperative seeding)", table_cell),
P("S. aureus, CoNS, gram-negative bacilli, Candida", table_cell),
P("Worse; high mortality", table_cell)],
[P("Late PVE", table_cell_c), P(">2 months after surgery", table_cell),
P("Same as NVE: S. aureus, viridans strep, enterococci", table_cell),
P("Better than early", table_cell)],
]
story.append(make_table(pve_data, [cw3*2, cw3*3.5, cw3*5, cw3*3.5]))
story.append(SP(0.4))
# ═══════════════════════════════════════════════════════
# SECTION 5: RISK FACTORS
# ═══════════════════════════════════════════════════════
story.append(SectionHeader("5. RISK FACTORS & PREDISPOSING CONDITIONS"))
story.append(SP(0.3))
rf_high = [
"Mitral valve prolapse with regurgitation - <b>now the leading predisposing condition</b> in developed countries",
"Previous episode of IE - confers high <b>lifelong risk</b> of recurrence",
"Prosthetic cardiac valve or prosthetic valve repair material - accounts for <b>10-20% of all IE</b>",
"IV drug use - endothelial damage + frequent bacteremia; tricuspid valve predominates",
"Congenital heart disease: bicuspid aortic valve, VSD (especially uncorrected), cyanotic CHD",
"Degenerative valvular disease (aortic stenosis, myxomatous mitral valve)",
"Intravascular devices: pacemaker leads, ICD leads, central venous catheters, TAVI valves",
"Chronic hemodialysis (arteriovenous fistulas reduce risk vs tunneled catheters)",
"Ventricular assist devices and implantable artificial hearts (added as high-risk in AHA 2021)",
]
rf_host = [
"Immunodeficiency (HIV, transplant, chemotherapy)",
"Diabetes mellitus",
"Malignancy (underlying GI neoplasm with S. bovis/gallolyticus)",
"Chronic alcohol use",
"Neutropenia",
]
story.append(SubHeader("High-Risk Cardiac Conditions"))
story.append(SP(0.15))
for r in rf_high:
story.append(B(r))
story.append(SP(0.2))
story.append(SubHeader("Host Factors Increasing Risk"))
story.append(SP(0.15))
for r in rf_host:
story.append(B(r))
story.append(SP(0.4))
# ═══════════════════════════════════════════════════════
# SECTION 6: CLINICAL FEATURES + IMAGES
# ═══════════════════════════════════════════════════════
story.append(SectionHeader("6. CLINICAL SYMPTOMS & SIGNS"))
story.append(SP(0.3))
story.append(ClinicalFeaturesDiagram())
story.append(SP(0.3))
# Peripheral stigmata table with images
story.append(SubHeader("6a. Peripheral Stigmata - Classic Signs"))
story.append(SP(0.2))
stig_data = [
[P("Sign", table_header), P("Description", table_header), P("Mechanism", table_header), P("Clinical Pearl", table_header)],
[P("Petechiae", table_cell_c),
P("Small red/brown non-blanching spots; skin, conjunctivae, oral mucosa", table_cell),
P("Embolic + vasculitic", table_cell),
P("Most common peripheral sign", table_cell)],
[P("Splinter Hemorrhages", table_cell_c),
P("Linear dark streaks under nail bed, parallel to nail growth", table_cell),
P("Septic microemboli", table_cell),
P("Non-specific; also in 5-10% of healthy hospitalized patients", table_cell)],
[P("Osler Nodes", table_cell_c),
P("Painful, tender, red-purple nodules on pads of fingers/toes", table_cell),
P("Immune complex vasculitis", table_cell),
P("O = Ouch (painful). More common in subacute IE", table_cell)],
[P("Janeway Lesions", table_cell_c),
P("Painless, flat, hemorrhagic macules on palms and soles", table_cell),
P("Septic emboli", table_cell),
P("J = Just there (painless). More common in acute S. aureus IE", table_cell)],
[P("Roth Spots", table_cell_c),
P("Oval retinal hemorrhages with white/pale centers", table_cell),
P("Immune complex vasculitis in retina", table_cell),
P("Seen on fundoscopy; also in leukemia, anemia", table_cell)],
[P("Clubbing", table_cell_c),
P("Bulbous enlargement of fingertips; loss of nail bed angle", table_cell),
P("Chronic hypoxia/inflammation", table_cell),
P("Seen in prolonged subacute IE only", table_cell)],
[P("Splenomegaly", table_cell_c),
P("Enlarged, tender spleen", table_cell),
P("Chronic infection + immune activation", table_cell),
P("More common in subacute IE", table_cell)],
]
story.append(make_table(stig_data, [cw3*2.5, cw3*4, cw3*3, cw3*4.5]))
story.append(SP(0.4))
# Clinical images
story.append(SubHeader("6b. Clinical and Imaging Findings"))
story.append(SP(0.2))
img_flow = []
for key in ['petechiae', 'osler', 'echo_aortic', 'gross_valve']:
if local_imgs.get(key):
path, cap = local_imgs[key]
try:
im = Image(path, width=7.5*cm, height=5.5*cm, kind='proportional')
img_flow.append([im, P(cap, caption_style)])
except:
pass
if len(img_flow) >= 2:
img_tbl = Table(
[[img_flow[0][0], img_flow[1][0]],
[img_flow[0][1], img_flow[1][1]]],
colWidths=[(PAGE_W-4*cm)/2, (PAGE_W-4*cm)/2]
)
img_tbl.setStyle(TableStyle([
('ALIGN', (0,0), (-1,-1), 'CENTER'),
('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
('TOPPADDING', (0,0), (-1,-1), 4),
]))
story.append(img_tbl)
story.append(SP(0.2))
if len(img_flow) >= 4:
img_tbl2 = Table(
[[img_flow[2][0], img_flow[3][0]],
[img_flow[2][1], img_flow[3][1]]],
colWidths=[(PAGE_W-4*cm)/2, (PAGE_W-4*cm)/2]
)
img_tbl2.setStyle(TableStyle([
('ALIGN', (0,0), (-1,-1), 'CENTER'),
('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
('TOPPADDING', (0,0), (-1,-1), 4),
]))
story.append(img_tbl2)
story.append(SP(0.4))
# ═══════════════════════════════════════════════════════
# SECTION 7: DIAGNOSIS
# ═══════════════════════════════════════════════════════
story.append(PageBreak())
story.append(SectionHeader("7. DIAGNOSIS"))
story.append(SP(0.3))
story.append(SubHeader("7a. Modified Duke Criteria"))
story.append(SP(0.2))
story.append(DukeCriteriaDiagram())
story.append(SP(0.3))
story.append(SubHeader("7b. Diagnostic Workup"))
story.append(SP(0.2))
dx_data = [
[P("Investigation", table_header), P("Findings in IE", table_header), P("Clinical Significance", table_header)],
[P("Blood Cultures x3", table_cell_c),
P("Positive in 90%; persistently positive in bacteremia", table_cell),
P("CORNERSTONE - obtain before antibiotics; separate sites, 30 min apart", table_cell)],
[P("TTE (Transthoracic Echo)", table_cell_c),
P("Vegetation, valve regurgitation, abscess; sensitivity 40-60%", table_cell),
P("First-line; negative TTE does NOT rule out IE if suspicion high", table_cell)],
[P("TEE (Transesophageal Echo)", table_cell_c),
P("Higher resolution; sensitivity ~95%", table_cell),
P("Mandatory if TTE negative + high suspicion, prosthetic valve, or poor image quality", table_cell)],
[P("CBC", table_cell_c),
P("Leukocytosis (acute); normochromic anemia (subacute)", table_cell),
P("Anemia of chronic disease common in subacute IE", table_cell)],
[P("ESR / CRP", table_cell_c),
P("Markedly elevated", table_cell),
P("Non-specific but useful for monitoring treatment response", table_cell)],
[P("Urinalysis", table_cell_c),
P("Hematuria, proteinuria, RBC casts", table_cell),
P("Immune complex glomerulonephritis (minor Duke criterion)", table_cell)],
[P("ECG", table_cell_c),
P("New PR prolongation, AV block, bundle branch block", table_cell),
P("Suggests perivalvular extension / abscess - urgent TEE + surgery consult", table_cell)],
[P("CT/MRI Brain", table_cell_c),
P("Ischemic stroke, hemorrhagic transformation, mycotic aneurysm", table_cell),
P("Performed if neurologic symptoms; mycotic aneurysm needs vascular surgery referral", table_cell)],
[P("18F-FDG PET/CT", table_cell_c),
P("Increased uptake at valve; detects extracardiac embolic foci", table_cell),
P("Added as major Duke criterion in updated ESC/modified criteria; valuable in PVE", table_cell)],
[P("Rheumatoid Factor", table_cell_c),
P("False-positive (up to 50% in subacute IE)", table_cell),
P("Minor Duke criterion - immunologic phenomenon", table_cell)],
]
story.append(make_table(dx_data, [cw3*2.5, cw3*5, cw3*6.5]))
story.append(SP(0.4))
# ═══════════════════════════════════════════════════════
# SECTION 8: TREATMENT
# ═══════════════════════════════════════════════════════
story.append(SectionHeader("8. TREATMENT - COMPLETE PLAN"))
story.append(SP(0.3))
story.append(InfoBox(
lines=[
"IE requires INPATIENT management. A multidisciplinary team (Infectious Disease + Cardiology + Cardiac Surgery) is MANDATORY.",
"ALWAYS obtain 3 blood cultures before starting antibiotics (unless hemodynamically unstable).",
"Standard IV antibiotic course: 4-6 weeks, counting from the day bacteremia is confirmed cleared.",
"Monitor blood cultures DAILY until clearance is documented.",
"Clinical improvement typically seen within 3-10 days of initiating therapy.",
],
color=LIGHT_BLUE,
border_color=MED_BLUE,
title="TREATMENT PRINCIPLES"
))
story.append(SP(0.3))
story.append(TreatmentFlowchart())
story.append(SP(0.3))
story.append(SubHeader("8a. Antibiotic Therapy - Organism-Directed"))
story.append(SP(0.2))
abx_data = [
[P("Organism", table_header), P("Regimen", table_header), P("Duration", table_header), P("Notes", table_header)],
# Streptococci
[P("Viridans Streptococci\n(PCN-sensitive MIC ≤0.12)", table_cell),
P("Penicillin G 12-18 MU/day IV continuous OR\nCeftriaxone 2g IV/IM once daily", table_cell),
P("4 weeks\n(NVE)", table_cell),
P("Can use 2-week regimen with gentamicin if uncomplicated", table_cell)],
[P("Viridans Streptococci\n(PCN-resistant MIC >0.12)", table_cell),
P("Penicillin G or Ampicillin\n+ Gentamicin (3mg/kg/day in 3 doses)", table_cell),
P("4-6 weeks", table_cell),
P("Gentamicin for first 2 weeks only to limit nephrotoxicity", table_cell)],
# S. aureus
[P("S. aureus - MSSA\n(Native valve)", table_cell),
P("Nafcillin or Oxacillin 12g/day IV (6 divided doses)\nOR Cefazolin 6g/day IV", table_cell),
P("6 weeks", table_cell),
P("DO NOT add routine gentamicin (no benefit, adds nephrotoxicity)", table_cell)],
[P("S. aureus - MRSA\n(Native valve)", table_cell),
P("Vancomycin 15-20 mg/kg IV q8-12h\nOR Daptomycin ≥8-10 mg/kg IV once daily", table_cell),
P("6 weeks", table_cell),
P("Vancomycin trough goal 15-20 mcg/mL; monitor CK with daptomycin", table_cell)],
[P("S. aureus - MSSA\n(Prosthetic valve)", table_cell),
P("Oxacillin/Nafcillin + Rifampin 300mg PO q8h\n+ Gentamicin 3mg/kg/day IV (2-3 doses)", table_cell),
P("≥6 weeks\n(Gent x 2 wks only)", table_cell),
P("Rifampin improves biofilm penetration; essential for prosthetic valves", table_cell)],
[P("S. aureus - MRSA\n(Prosthetic valve)", table_cell),
P("Vancomycin + Rifampin 300mg PO q8h\n+ Gentamicin 3mg/kg/day IV (2-3 doses)", table_cell),
P("≥6 weeks\n(Gent x 2 wks only)", table_cell),
P("Rifampin + gentamicin combo critical for PVE biofilm eradication", table_cell)],
# Enterococcus
[P("Enterococcus\n(PCN-sensitive)", table_cell),
P("Ampicillin 12g/day IV + Gentamicin 3mg/kg/day\nOR Ampicillin 12g/day + Ceftriaxone 4g/day", table_cell),
P("4-6 weeks", table_cell),
P("Ampicillin-Ceftriaxone preferred in renal impairment (avoids aminoglycosides)", table_cell)],
[P("Enterococcus - VRE", table_cell),
P("Linezolid 600mg IV/PO q12h\nOR Daptomycin ≥8 mg/kg IV\n(ID consultation mandatory)", table_cell),
P("≥6 weeks", table_cell),
P("High treatment failure rate; surgical intervention often required", table_cell)],
# HACEK
[P("HACEK Group\nOrganisms", table_cell),
P("Ceftriaxone 2g IV/IM once daily\nAlternatives: Ampicillin 12g/day or\nCiprofloxacin 400mg IV q12h", table_cell),
P("4 weeks (NVE)\n6 weeks (PVE)", table_cell),
P("Most HACEK organisms now beta-lactamase positive; avoid ampicillin alone", table_cell)],
# Fungal
[P("Fungal IE\n(Candida / Aspergillus)", table_cell),
P("Amphotericin B liposomal 3-5mg/kg/day\n+ Flucytosine; then long-term azole suppression", table_cell),
P("≥6 weeks + lifelong\nsuppression", table_cell),
P("Surgery almost always required; Aspergillus rarely grows in blood cultures", table_cell)],
# Culture-negative
[P("Culture-Negative IE", table_cell),
P("Consult Infectious Diseases urgently.\nConsider: Ceftriaxone + Ampicillin OR\nDoxycycline for Coxiella/Bartonella", table_cell),
P("4-6 weeks\n(organism-dependent)", table_cell),
P("Serologic testing for Coxiella (Q fever), Bartonella, Brucella; consider PCR of excised valve", table_cell)],
]
story.append(make_table(abx_data, [cw3*2.5, cw3*4.5, cw3*2, cw3*5]))
story.append(SP(0.3))
story.append(InfoBox(
lines=[
"Monitoring: Vancomycin trough goal 15-20 mcg/mL (or AUC/MIC 400-600).",
"Aminoglycosides: Baseline + weekly audiometry if course >7 days. Monitor serum levels.",
"Daptomycin: Monitor serum CK. Avoid if concurrent statins where possible.",
"Rifampin: Never use alone (rapid resistance development); always combine.",
"Oral switch: Can be considered in carefully selected stable patients after initial IV course (POET trial evidence).",
],
color=LIGHT_YELLOW,
border_color=ORANGE,
title="MONITORING & SPECIAL CONSIDERATIONS"
))
story.append(SP(0.3))
# ═══════════════════════════════════════════════════════
# SECTION 8b: SURGICAL TREATMENT
# ═══════════════════════════════════════════════════════
story.append(SubHeader("8b. Surgical Indications"))
story.append(SP(0.2))
surg_data = [
[P("Indication", table_header), P("Timing", table_header), P("NVE", table_header), P("PVE", table_header)],
[P("Acute heart failure from valve dysfunction", table_cell),
P("EMERGENCY\n(same admission)", table_cell),
P("Yes - most common\n& urgent indication", table_cell),
P("Yes", table_cell)],
[P("Perivalvular extension (abscess, fistula, new heart block)", table_cell),
P("URGENT\n(within days)", table_cell),
P("Yes", table_cell),
P("Yes - very common in PVE", table_cell)],
[P("Uncontrolled infection (persistent bacteremia >5-7 days despite adequate abx)", table_cell),
P("URGENT", table_cell),
P("Yes", table_cell),
P("Yes", table_cell)],
[P("Fungal or highly resistant organism (MRSA, VRE)", table_cell),
P("URGENT", table_cell),
P("Yes", table_cell),
P("Yes - often required", table_cell)],
[P("Large mobile vegetation >10mm + prior embolic event", table_cell),
P("URGENT\n(within 24-48h)", table_cell),
P("Yes", table_cell),
P("Yes", table_cell)],
[P("Very large vegetation >15mm (even without prior embolism)", table_cell),
P("URGENT", table_cell),
P("Consider", table_cell),
P("Consider", table_cell)],
[P("Increasing vegetation size on optimal antibiotics", table_cell),
P("ELECTIVE/URGENT", table_cell),
P("Consider", table_cell),
P("Yes", table_cell)],
[P("Valve dehiscence / obstruction / severe regurgitation (PVE)", table_cell),
P("URGENT", table_cell),
P("N/A", table_cell),
P("Yes - hallmark PVE indication", table_cell)],
[P("Right-sided IE (IDU)", table_cell),
P("Elective (if fails medical therapy)", table_cell),
P("Usually avoid\n(tricuspid valve IE)", table_cell),
P("N/A", table_cell)],
]
story.append(make_table(surg_data, [cw3*4, cw3*2.5, cw3*2.5, cw3*5]))
story.append(SP(0.2))
story.append(InfoBox(
lines=[
"Neurologic complications: Delay surgery 3-4 weeks after major ISCHEMIC stroke.",
"Hemorrhagic stroke: Surgery is CONTRAINDICATED within 1 month.",
"Silent cerebral emboli (found on imaging only): Do NOT delay surgery.",
"All IE patients should be managed at centers with expertise in cardiac surgery and ID.",
],
color=LIGHT_RED,
border_color=RED,
title="SURGICAL TIMING - NEUROLOGIC COMPLICATIONS"
))
story.append(SP(0.4))
# ═══════════════════════════════════════════════════════
# SECTION 9: COMPLICATIONS
# ═══════════════════════════════════════════════════════
story.append(SectionHeader("9. COMPLICATIONS"))
story.append(SP(0.3))
story.append(ComplicationDiagram())
story.append(SP(0.3))
comp_data = [
[P("Complication", table_header), P("Frequency", table_header), P("Details & Management", table_header)],
[P("Heart Failure", table_cell_c), P("30-40%", table_cell),
P("Most common cause of death. Aortic valve IE has highest risk. Acute severe regurgitation → emergent surgery.", table_cell)],
[P("Perivalvular Abscess", table_cell_c), P("20-40% (more in S. aureus)", table_cell),
P("Presents with persistent fever, new conduction block on ECG. Requires TEE + urgent surgery.", table_cell)],
[P("Embolic Stroke / TIA", table_cell_c), P("15-20%", table_cell),
P("Most common in first 2 weeks; risk drops after 2 weeks of antibiotics. Mitral valve IE has highest stroke risk.", table_cell)],
[P("Septic Pulmonary Emboli", table_cell_c), P("Common in right-sided IE", table_cell),
P("Chest pain, hemoptysis, patchy infiltrates on CXR. Multiple cavitating lesions on CT.", table_cell)],
[P("Mycotic Aneurysm", table_cell_c), P("2-10%", table_cell),
P("Infected emboli lodge in vessel wall; intracranial location is most feared (rupture → hemorrhage). CT/MR angiography + vascular surgery.", table_cell)],
[P("Glomerulonephritis", table_cell_c), P("Common in subacute IE", table_cell),
P("Immune complex deposition. Hematuria, proteinuria, casts. Usually resolves with treatment.", table_cell)],
[P("Metastatic Infection", table_cell_c), P("Variable", table_cell),
P("Vertebral osteomyelitis, psoas abscess, meningitis, septic arthritis. Prolonged antibiotics + drainage as needed.", table_cell)],
[P("Renal Failure", table_cell_c), P("10-20%", table_cell),
P("From emboli, immune complex nephritis, or drug toxicity (aminoglycosides, vancomycin). Monitor creatinine.", table_cell)],
[P("Relapse", table_cell_c), P("2-6%", table_cell),
P("Risk higher with inadequate therapy, IV drug continuation, or resistant organisms. Full retreatment required.", table_cell)],
]
story.append(make_table(comp_data, [cw3*2.5, cw3*2, cw3*9.5]))
story.append(SP(0.4))
# ═══════════════════════════════════════════════════════
# SECTION 10: PROPHYLAXIS
# ═══════════════════════════════════════════════════════
story.append(SectionHeader("10. PROPHYLAXIS (AHA 2021 Updated Guidelines)"))
story.append(SP(0.3))
story.append(P(
"Antibiotic prophylaxis before <b>invasive dental procedures</b> is recommended ONLY for the highest-risk patients. "
"The 2021 AHA update made several important changes from the 2007 guidelines:"
))
story.append(SP(0.15))
aha21 = [
"<b>Added to high-risk list:</b> Ventricular assist devices and implantable artificial hearts",
"<b>Eliminated clindamycin</b> as alternative (risk of Clostridioides difficile - potentially fatal)",
"<b>Added doxycycline</b> as alternative for truly penicillin-allergic patients",
"<b>Allergy screening:</b> 90% of patients reporting penicillin allergy have negative skin testing - amoxicillin can be used",
"<b>Shared decision-making</b> with patient education regarding prophylaxis use",
"<b>Oral hygiene:</b> Maintaining good oral health may reduce viridans streptococcal bacteremia from daily activities",
]
for a in aha21:
story.append(B(a))
story.append(SP(0.2))
proph_data = [
[P("High-Risk Group", table_header), P("Examples", table_header)],
[P("Prosthetic cardiac valve / repair material", table_cell),
P("Mechanical valve, bioprosthetic valve, TAVI valve, annuloplasty ring", table_cell)],
[P("Previous infective endocarditis", table_cell),
P("Any prior episode - lifetime prophylaxis", table_cell)],
[P("Congenital heart disease (CHD)", table_cell),
P("Unrepaired cyanotic CHD; Repaired CHD with residual shunt/regurgitation near prosthetic material", table_cell)],
[P("Cardiac transplant with valvulopathy", table_cell),
P("Valve disease developing after heart transplantation", table_cell)],
[P("Ventricular assist devices (VAD)", table_cell),
P("LVAD, RVAD, BiVAD, total artificial heart (added in 2021)", table_cell)],
]
story.append(make_table(proph_data, [cw3*4, cw3*10]))
story.append(SP(0.2))
story.append(SubHeader("Prophylaxis Regimens"))
story.append(SP(0.15))
reg_data = [
[P("Situation", table_header), P("Drug", table_header), P("Dose & Timing", table_header)],
[P("Oral / PCN-tolerant", table_cell_c),
P("Amoxicillin (preferred)", table_cell),
P("2g PO, 30-60 minutes before procedure", table_cell)],
[P("Unable to take oral", table_cell_c),
P("Ampicillin OR Cefazolin OR Ceftriaxone", table_cell),
P("2g IM/IV (Ampicillin) or 1g IM/IV (Cefazolin/Ceftriaxone) 30-60 min before", table_cell)],
[P("PCN/Ampicillin allergic\n(oral)", table_cell_c),
P("Cephalexin OR Azithromycin OR Clarithromycin OR Doxycycline (NEW 2021)", table_cell),
P("Cephalexin 2g PO; Azithromycin/Clarithromycin 500mg PO; Doxycycline 100mg PO", table_cell)],
[P("PCN allergic\n(unable oral)", table_cell_c),
P("Cefazolin OR Ceftriaxone", table_cell),
P("1g IM/IV 30-60 min before", table_cell)],
]
story.append(make_table(reg_data, [cw3*2.5, cw3*4.5, cw3*7]))
story.append(SP(0.4))
# ═══════════════════════════════════════════════════════
# SECTION 11: BASIC-TO-CLINICAL CORRELATION TABLE
# ═══════════════════════════════════════════════════════
story.append(PageBreak())
story.append(SectionHeader("11. BASIC SCIENCE TO CLINICAL CORRELATION"))
story.append(SP(0.3))
btc_data = [
[P("Basic Science Concept", table_header), P("Clinical Correlate", table_header), P("Practical Implication", table_header)],
[P("NBTE: sterile thrombus on\ndamaged endothelium", table_cell),
P("Predisposing cardiac conditions (MVP, rheumatic, bicuspid AV)", table_cell),
P("Screen at-risk patients; antibiotic prophylaxis for dental procedures", table_cell)],
[P("Bacterial adhesins (FnBP, MSCRAMMs)\nbind collagen/fibrin matrix", table_cell),
P("Viridans strep after dental work seeds damaged valves; S. aureus after IV access", table_cell),
P("Dental hygiene, aseptic IV technique reduce IE incidence", table_cell)],
[P("Vegetation = fibrin + platelets +\nbacteria + inflammatory cells", table_cell),
P("Echogenic, mobile mass on echocardiogram with acoustic shadowing", table_cell),
P("Echo is mandatory; TEE more sensitive for small/prosthetic valve vegetations", table_cell)],
[P("Deep bacteria within biofilm:\nprotected from antibiotics", table_cell),
P("Requires 4-6 weeks of HIGH-DOSE IV antibiotics", table_cell),
P("Oral therapy generally inadequate; rifampin added for PVE biofilm penetration", table_cell)],
[P("Vegetation fragments embolize\nto systemic circulation", table_cell),
P("Stroke (15-20%), renal/splenic infarcts, septic pulmonary emboli", table_cell),
P("Anticoagulation contraindicated; surgery if large vegetations (>10mm + prior emboli)", table_cell)],
[P("Immune complex (IgG + complement)\ndeposition in microvasculature", table_cell),
P("Osler nodes, Roth spots, glomerulonephritis, RF positive (minor Duke criteria)", table_cell),
P("Seen predominantly in subacute IE; not a sign of active septic emboli", table_cell)],
[P("Local extension of vegetation\nthrough annulus and myocardium", table_cell),
P("New heart block on ECG; perivalvular abscess on TEE; ring abscess at surgery", table_cell),
P("New PR prolongation = cardiac emergency - urgent TEE + surgery consult", table_cell)],
[P("Persistent bacteremia with\nbiofilm-protected organisms", table_cell),
P("Daily blood cultures until clearance; persistent fever on therapy = complication", table_cell),
P("Fever after 72h on correct antibiotics: check for abscess, septic emboli, drug fever", table_cell)],
[P("S. gallolyticus/bovis: colonic\nmucosa as portal of entry", table_cell),
P("IE from this organism = mandatory GI screening", table_cell),
P("Colonoscopy required in ALL S. gallolyticus/bovis IE and enterococcal IE", table_cell)],
[P("Rickettsiae (Coxiella burnetti)\ndo not grow in standard culture", table_cell),
P("Culture-negative IE in farmers, abattoir workers, rural setting", table_cell),
P("Phase II Coxiella IgG titer >1:800 is a major Duke criterion (Q fever endocarditis)", table_cell)],
]
story.append(make_table(btc_data, [cw3*3.5, cw3*4.5, cw3*6]))
story.append(SP(0.4))
# ═══════════════════════════════════════════════════════
# SECTION 12: QUICK REFERENCE SUMMARY
# ═══════════════════════════════════════════════════════
story.append(SectionHeader("12. QUICK REFERENCE SUMMARY"))
story.append(SP(0.3))
story.append(InfoBox(
lines=[
"DEFINITION: Microbial infection of cardiac valve endothelium → vegetation formation",
"MOST COMMON CAUSE (high-income countries): Staphylococcus aureus",
"MOST COMMON CAUSE (developing countries, community): Viridans streptococci",
"MOST CONSISTENT SYMPTOM: Fever (>90%)",
"GOLD STANDARD DIAGNOSIS: Blood culture + vegetation on echocardiography",
"DIAGNOSIS CRITERIA: Modified Duke Criteria (sensitivity 76-100%, specificity 88-100%)",
"FIRST-LINE ECHO: TTE; upgrade to TEE if TTE negative + high suspicion or prosthetic valve",
"TREATMENT: Organism-directed IV antibiotics 4-6 weeks + multidisciplinary team",
"MOST COMMON SURGERY INDICATION: Acute heart failure from valve dysfunction",
"PROPHYLAXIS: AHA 2021 - amoxicillin 2g PO before invasive dental procedures (high-risk only)",
"MORTALITY: ~25% at 90 days; worse with S. aureus, HF, perivalvular extension",
],
color=LIGHT_BLUE,
border_color=DARK_BLUE,
title="KEY FACTS AT A GLANCE"
))
story.append(SP(0.3))
# References
story.append(HRFlowable(width='100%', thickness=0.5, color=GRAY_BORDER))
story.append(SP(0.2))
story.append(P("<b>SOURCES:</b>", h3_style))
refs = [
"Robbins & Kumar Basic Pathology, 10th Ed. - Infective Endocarditis, p. 368-369",
"Braunwald's Heart Disease, 12th Ed. (9780323722193) - Chapter 80G: IE Prevention, Diagnosis & Management",
"Goldman-Cecil Medicine International Edition (9780323930345) - Chapter 61: Infective Endocarditis",
"Washington Manual of Medical Therapeutics (9781975190620) - Chapter 14: Infective Endocarditis",
"Rosen's Emergency Medicine (9780323757898) - Chapter 69: Infective Endocarditis",
"AHA 2021 Scientific Statement: Prevention of Viridans Group Streptococcal Infective Endocarditis",
"ESC Guidelines on Prevention, Diagnosis and Treatment of Infective Endocarditis (2023 update)",
"Li JS et al. Proposed modifications to Duke criteria. Clin Infect Dis. 2000;30(4):633-638",
]
for ref in refs:
story.append(P('• ' + ref, note_style))
story.append(SP(0.3))
story.append(P(
'<i>Generated by Orris Medical AI · August 2026 · For educational purposes only. '
'Always refer to current institutional guidelines and consult specialists for patient care decisions.</i>',
caption_style
))
# ─────────────────────────────────────────────
# PAGE TEMPLATE WITH HEADER/FOOTER
# ─────────────────────────────────────────────
def on_page(canvas, document):
canvas.saveState()
# Header bar
canvas.setFillColor(DARK_BLUE)
canvas.rect(0, PAGE_H - 1.5*cm, PAGE_W, 1.5*cm, fill=1, stroke=0)
canvas.setFillColor(YELLOW)
canvas.rect(0, PAGE_H - 1.5*cm, 5, 1.5*cm, fill=1, stroke=0)
canvas.setFillColor(WHITE)
canvas.setFont('Helvetica-Bold', 9)
canvas.drawString(1.5*cm, PAGE_H - 1.0*cm, "INFECTIVE ENDOCARDITIS — Complete Clinical Reference")
canvas.setFont('Helvetica', 8)
canvas.setFillColor(colors.HexColor('#A0C8E8'))
canvas.drawRightString(PAGE_W - 1.5*cm, PAGE_H - 1.0*cm, "Orris Medical AI · August 2026")
# Footer
canvas.setFillColor(GRAY_BG)
canvas.rect(0, 0, PAGE_W, 1.2*cm, fill=1, stroke=0)
canvas.setFillColor(MED_BLUE)
canvas.rect(0, 1.2*cm, PAGE_W, 1.5, fill=1, stroke=0)
canvas.setFillColor(DARK_BLUE)
canvas.setFont('Helvetica', 7.5)
canvas.drawString(1.5*cm, 0.45*cm,
"Sources: Robbins Pathology · Braunwald's Heart Disease · Goldman-Cecil Medicine · Washington Manual · Rosen's Emergency Medicine")
canvas.setFont('Helvetica-Bold', 8)
canvas.drawRightString(PAGE_W - 1.5*cm, 0.45*cm, f"Page {document.page}")
canvas.restoreState()
print("Building PDF...")
doc.build(story, onFirstPage=on_page, onLaterPages=on_page)
print(f"PDF saved to: {out_path}")
cd /tmp/workspace/ie-pdf && python generate_ie_pdf.py 2>&1
ls -lh /tmp/workspace/ie-pdf/Infective_Endocarditis_Complete_Guide.pdf
Infective Endocarditis Complete Guide
PDF Document · PDF
Infective endocarditis clinical symptoms causes and treatment and basic to clinical Start basic to clinical and convert into pdf
mkdir -p /tmp/workspace/ie-btc/imgs && echo "ready"
Use Harrison 21th edition with Infective endocarditis clinical symptoms causes and treatment and basic to clinical Start basic to clinical and convert into pdf
infective endocarditis peripheral signs Osler nodes Janeway lesions Roth spots splinter hemorrhage

This clinical photograph shows the palmar surface of a right hand, illustrating classic peripheral stigmata of infective endocarditis. The image displays multiple Janeway lesions, which appear as pink-to-red, macular, irregularly shaped, and non-tender hemorrhagic spots. These lesions are notably prominent on the distal phalanx of the thumb and the middle finger (indicated by arrows). Additionally, a clear subungual splinter hemorrhage is visible as a dark, linear longitudinal streak under the nail bed of the thumb (indicated by an arrowhead). The skin also shows signs of generalized dryness and scaling. These visual findings are critical clinical indicators of septic emboli or immunologic phenomena associated with systemic infection, specifically infective endocarditis. The photograph serves as an educational tool for recognizing vascular manifestations during a physical examination in an acute care or cardiology context.

This composite clinical photograph displays the physical findings of infective endocarditis manifest in the hands. Panel A shows a normal right palm for comparison. Panel B illustrates the left palm with multiple erythematous to violaceous lesions located on the thenar and hypothenar eminences. Panel C provides a magnified view of these palm lesions, distinguishing between Osler's nodes—small, raised, tender, reddish-purple nodules (marked with an arrow)—and Janeway lesions, which are flat, non-tender, erythematous macules (marked with an arrowhead). Panel D compares the nailbeds, highlighting a splinter hemorrhage on the left digit, characterized by a small, linear, dark-red streak beneath the nail plate, while the right nailbed appears normal. These peripheral stigmata are classic clinical indicators of septic emboli or immunological responses associated with systemic infections like endocarditis or infected arteriovenous fistulas.

This clinical photograph displays characteristic peripheral stigmata of infective endocarditis on a patient's hand. The primary findings are Osler's nodes, seen as painful, raised, violaceous nodular lesions located on the distal aspect of the fourth digit. These nodules exhibit a deep red-to-purple hue and appear slightly edematous, indicative of immune complex-mediated vasculitis. Additionally, the index finger displays splinter hemorrhages, visible as small, linear, dark reddish-brown streaks beneath the nail bed, representing micro-emboli or capillary damage. The middle finger is partially occluded by a patterned medical adhesive bandage. A healthcare provider in purple nitrile gloves is holding the hand, facilitating the clinical examination. These visual signs are critical diagnostic indicators for bacterial endocarditis, often associated with systemic bacteremia and valvular vegetations. The image serves as an educational tool for identifying classic dermatological manifestations of systemic cardiovascular infection.

This clinical photograph displays the palmar surface of a hand and fingers, highlighting classic dermatological manifestations of Janeway lesions. The image shows multiple, small, non-tender, erythematous macular rashes and hemorrhagic spots. These lesions are primarily distributed along the distal aspects of the fingers, specifically on the tips and pads. Morphologically, the lesions appear as flat, irregular, reddish-purple spots that do not involve significant skin elevation or surrounding edema. Clinically, these findings are pathognomonic for infective endocarditis, representing septic microemboli that lead to vascular injury and subcutaneous hemorrhage. The surrounding skin texture remains otherwise unremarkable, lacking signs of scaling or diffuse inflammation. This visual is an essential educational tool for identifying peripheral stigmata of systemic infection and differentiating painless Janeway lesions from painful, raised Osler nodules.
infective endocarditis echocardiogram vegetation mitral aortic valve

This composite figure presents clinical imaging and surgical findings of infective endocarditis. (A) Transthoracic echocardiogram (TTE) in a long-axis view shows a large, pedunculated echogenic vegetation (white arrow) measuring 1.75 cm x 1.10 cm attached to the anterior mitral valve leaflet. (B) TTE with color Doppler demonstrates a mosaic-pattern jet across the mitral valve, indicating severe mitral regurgitation. (C) Intraoperative view through the aorta (AO) reveals a yellowish vegetation (white arrow) infiltrating the aortic valve cusp. (D) Surgical exposure of the mitral valve via the right atrium (RA) shows extensive friable, yellowish vegetations (arrows) involving the anterior mitral cusp and annulus. (E) Gross specimen photograph displays the excised material on surgical gauze, with arrows pointing to the vegetations associated with the aortic valve (AV) and anterior mitral valve (AMV) tissue. The images collectively demonstrate the progression from diagnostic ultrasonography to surgical management of multi-valvular endocarditis, highlighting the morphology and destructive nature of infective vegetations.

Two side-by-side transesophageal echocardiogram (TEE) images, labeled A and B, demonstrating infective endocarditis with valvular vegetations. Image A shows a grayscale ultrasound view of the mitral valve, with a yellow circle highlighting an irregular, heterogeneously echogenic mass (vegetation) attached to the mitral leaflet. Image B displays the aortic valve region, where a similar yellow circle indicates a lobulated, hyperechoic vegetation adherent to the aortic valve leaflets. Both structures exhibit typical characteristics of cardiac vegetations, including surface irregularities and moderate-to-high echogenicity relative to the myocardium. The surrounding valvular tissue in image B appears more disrupted, consistent with advanced infectious involvement. These diagnostic images are used to illustrate the visual manifestations of infective endocarditis, specifically the identification of vegetations on different heart valves, which are critical for confirming a clinical diagnosis of sepsis secondary to bacteremia.
mkdir -p /tmp/workspace/ie-harrison/imgs && echo "ready"