~/tetanus-card/generate_card.py
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_RIGHT
from reportlab.platypus import Flowable
# βββ Colour palette βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
DARK_BLUE = colors.HexColor("#1a3a5c")
MED_BLUE = colors.HexColor("#2e6da4")
LIGHT_BLUE = colors.HexColor("#d6e8f7")
ACCENT_RED = colors.HexColor("#c0392b")
ACCENT_GRN = colors.HexColor("#1e7e34")
ACCENT_ORG = colors.HexColor("#e67e22")
HEADER_BG = colors.HexColor("#1a3a5c")
ROW_ALT = colors.HexColor("#f0f6fc")
WHITE = colors.white
LIGHT_GREY = colors.HexColor("#f5f5f5")
BORDER = colors.HexColor("#b0c4de")
# βββ Document setup ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
OUTPUT = "/home/daytona/workspace/tetanus-card/Tetanus_Quick_Reference.pdf"
doc = SimpleDocTemplate(
OUTPUT,
pagesize=A4,
leftMargin=1.2*cm, rightMargin=1.2*cm,
topMargin=1.0*cm, bottomMargin=1.0*cm
)
W, H = A4
CONTENT_W = W - 2.4*cm
styles = getSampleStyleSheet()
# βββ Custom styles βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
def S(name, **kw):
return ParagraphStyle(name, **kw)
title_style = S("Title2",
fontSize=18, leading=22, textColor=WHITE,
fontName="Helvetica-Bold", alignment=TA_CENTER)
subtitle_style = S("Subtitle",
fontSize=9, leading=11, textColor=colors.HexColor("#cce0f5"),
fontName="Helvetica", alignment=TA_CENTER)
sec_head = S("SecHead",
fontSize=9, leading=11, textColor=WHITE,
fontName="Helvetica-Bold", alignment=TA_LEFT,
spaceBefore=0, spaceAfter=0)
body = S("Body",
fontSize=7.5, leading=10, textColor=colors.black,
fontName="Helvetica", spaceBefore=1, spaceAfter=1)
body_bold = S("BodyBold",
fontSize=7.5, leading=10, textColor=DARK_BLUE,
fontName="Helvetica-Bold", spaceBefore=1, spaceAfter=1)
bullet = S("Bullet",
fontSize=7.5, leading=10, textColor=colors.black,
fontName="Helvetica", leftIndent=8, spaceBefore=1, spaceAfter=1,
bulletIndent=0)
warning = S("Warning",
fontSize=7.5, leading=10, textColor=ACCENT_RED,
fontName="Helvetica-Bold", spaceBefore=1, spaceAfter=1)
footer_style = S("Footer",
fontSize=6.5, leading=8, textColor=colors.grey,
fontName="Helvetica", alignment=TA_CENTER)
# βββ Helper: section header ββββββββββββββββββββββββββββββββββββββββββββββββββ
def section_header(text, bg=MED_BLUE):
data = [[Paragraph(text, sec_head)]]
t = Table(data, colWidths=[CONTENT_W])
t.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), bg),
("TOPPADDING", (0,0), (-1,-1), 3),
("BOTTOMPADDING",(0,0), (-1,-1), 3),
("LEFTPADDING", (0,0), (-1,-1), 6),
("RIGHTPADDING", (0,0), (-1,-1), 6),
("ROUNDEDCORNERS",(0,0),(-1,-1), 2),
]))
return t
# βββ Helper: two-column layout wrapper ββββββββββββββββββββββββββββββββββββββ
def two_col(left_items, right_items, left_w_frac=0.5):
lw = CONTENT_W * left_w_frac - 2*mm
rw = CONTENT_W * (1 - left_w_frac) - 2*mm
left_tbl = Table([[item] for item in left_items], colWidths=[lw])
right_tbl = Table([[item] for item in right_items], colWidths=[rw])
for t in [left_tbl, right_tbl]:
t.setStyle(TableStyle([
("TOPPADDING", (0,0),(-1,-1), 0),
("BOTTOMPADDING",(0,0),(-1,-1), 0),
("LEFTPADDING", (0,0),(-1,-1), 0),
("RIGHTPADDING", (0,0),(-1,-1), 0),
]))
outer = Table([[left_tbl, right_tbl]], colWidths=[lw + 2*mm, rw + 2*mm])
outer.setStyle(TableStyle([
("VALIGN", (0,0),(-1,-1), "TOP"),
("TOPPADDING", (0,0),(-1,-1), 0),
("BOTTOMPADDING",(0,0),(-1,-1), 0),
("LEFTPADDING", (0,0),(-1,-1), 0),
("RIGHTPADDING",(0,0),(-1,-1), 2),
]))
return outer
# βββ Helper: plain table with header ββββββββββββββββββββββββββββββββββββββββ
def plain_table(header_row, rows, col_widths, hdr_bg=DARK_BLUE):
data = [header_row] + rows
t = Table(data, colWidths=col_widths, repeatRows=1)
style = [
# Header
("BACKGROUND", (0,0), (-1,0), hdr_bg),
("TEXTCOLOR", (0,0), (-1,0), WHITE),
("FONTNAME", (0,0), (-1,0), "Helvetica-Bold"),
("FONTSIZE", (0,0), (-1,0), 7),
("BOTTOMPADDING",(0,0), (-1,0), 3),
("TOPPADDING", (0,0), (-1,0), 3),
# Body
("FONTNAME", (0,1), (-1,-1), "Helvetica"),
("FONTSIZE", (0,1), (-1,-1), 7),
("TOPPADDING", (0,1), (-1,-1), 2),
("BOTTOMPADDING",(0,1), (-1,-1), 2),
("LEFTPADDING", (0,0), (-1,-1), 4),
("RIGHTPADDING", (0,0), (-1,-1), 4),
("GRID", (0,0), (-1,-1), 0.3, BORDER),
("VALIGN", (0,0), (-1,-1), "MIDDLE"),
]
for i in range(1, len(data)):
if i % 2 == 0:
style.append(("BACKGROUND", (0,i), (-1,i), ROW_ALT))
t.setStyle(TableStyle(style))
return t
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# BUILD CONTENT
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
story = []
# ββ TITLE BANNER βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
title_data = [
[Paragraph("TETANUS", title_style)],
[Paragraph("Quick Reference Card | MBBS Clinical Guide", subtitle_style)],
]
title_tbl = Table(title_data, colWidths=[CONTENT_W])
title_tbl.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), HEADER_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),
("ROUNDEDCORNERS",(0,0),(-1,-1), 3),
]))
story.append(title_tbl)
story.append(Spacer(1, 4*mm))
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# ROW 1: Pathophysiology + Clinical Types (2 columns)
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# -- LEFT: Pathophysiology ---------------------------------------------------
patho_items = [
section_header("PATHOPHYSIOLOGY", MED_BLUE),
Spacer(1, 2),
Paragraph("<b>Organism:</b> Clostridium tetani β gram+ve, anaerobic, spore-forming rod", body),
Paragraph("<b>Toxin:</b> Tetanospasmin (neurotoxin)", body),
Paragraph("<b>Mechanism:</b>", body_bold),
Paragraph("1. Retrograde axonal transport to CNS motor nuclei", bullet),
Paragraph("2. Light chain (zinc endopeptidase) <b>cleaves VAMP-2</b>", bullet),
Paragraph("3. Blocks GABA & glycine release from inhibitory interneurons", bullet),
Paragraph("4. Unregulated motor & autonomic excitation", bullet),
Paragraph("<b>Toxins produced:</b> Tetanolysin (bacterial growth) + <b>Tetanospasmin</b> (all clinical features)", body),
Paragraph("<b>Does NOT cross BBB</b> β enters CNS via retrograde transport", warning),
Spacer(1, 3),
section_header("INCUBATION PERIOD", MED_BLUE),
Spacer(1, 2),
Paragraph("Range: <24 hours β >1 month (usually <b>7β21 days</b>)", body),
Paragraph("<b>Short incubation = severe disease & poor prognosis</b>", warning),
]
# -- RIGHT: Clinical Types ---------------------------------------------------
types_items = [
section_header("CLINICAL TYPES", MED_BLUE),
Spacer(1, 2),
]
types_tbl = plain_table(
[Paragraph("<b>Type</b>", S("th", fontSize=7, fontName="Helvetica-Bold", textColor=WHITE)),
Paragraph("<b>Features</b>", S("th", fontSize=7, fontName="Helvetica-Bold", textColor=WHITE))],
[
[Paragraph("Generalized (80%)", body_bold),
Paragraph("Most common. Trismus β risus sardonicus β opisthotonos β generalized spasms", body)],
[Paragraph("Local", body_bold),
Paragraph("Rigidity near wound only; usually resolves; ~1% fatal", body)],
[Paragraph("Cephalic", body_bold),
Paragraph("Head injury / otitis media; CN VII most common; poor prognosis", body)],
[Paragraph("Neonatal", body_bold),
Paragraph("Unvaccinated mother + unsterile cord; inability to suck, irritability by 2nd week", body)],
],
col_widths=[2.2*cm, 6.8*cm]
)
types_items.append(types_tbl)
types_items.append(Spacer(1, 3))
types_items.append(section_header("CLINICAL PROGRESSION", MED_BLUE))
types_items.append(Spacer(1, 2))
prog_tbl = plain_table(
[Paragraph("Trismus", S("th2", fontSize=7, fontName="Helvetica-Bold", textColor=WHITE)),
Paragraph("Risus sardonicus", S("th2", fontSize=7, fontName="Helvetica-Bold", textColor=WHITE)),
Paragraph("Dysphagia", S("th2", fontSize=7, fontName="Helvetica-Bold", textColor=WHITE)),
Paragraph("Opisth-otonos", S("th2", fontSize=7, fontName="Helvetica-Bold", textColor=WHITE)),
Paragraph("Autonomic storm (wk 2)", S("th2", fontSize=7, fontName="Helvetica-Bold", textColor=WHITE))],
[],
col_widths=[1.9*cm, 2.0*cm, 1.8*cm, 1.9*cm, 2.7*cm],
hdr_bg=ACCENT_ORG
)
types_items.append(prog_tbl)
types_items.append(Spacer(1, 2))
types_items.append(Paragraph("<b>Mental status is ALWAYS NORMAL</b> (unless hypoxia)", warning))
story.append(two_col(patho_items, types_items, left_w_frac=0.38))
story.append(Spacer(1, 3*mm))
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# ROW 2: Ablett Classification (full width)
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
story.append(KeepTogether([
section_header("ABLETT SEVERITY CLASSIFICATION", DARK_BLUE),
Spacer(1, 2),
plain_table(
[Paragraph("<b>Grade</b>", S("h",fontSize=7,fontName="Helvetica-Bold",textColor=WHITE)),
Paragraph("<b>Severity</b>", S("h",fontSize=7,fontName="Helvetica-Bold",textColor=WHITE)),
Paragraph("<b>Key Features</b>", S("h",fontSize=7,fontName="Helvetica-Bold",textColor=WHITE))],
[
[Paragraph("I", body_bold), Paragraph("Mild", body),
Paragraph("Mild trismus, general spasticity; NO respiratory compromise, NO spasms, NO dysphagia", body)],
[Paragraph("II", body_bold), Paragraph("Moderate", body),
Paragraph("Moderate trismus, rigidity, short spasms, mild dysphagia; RR >30/min", body)],
[Paragraph("III", body_bold), Paragraph("Severe", body),
Paragraph("Severe trismus, generalized rigidity, prolonged spasms, severe dysphagia, apneic spells; HR >120, RR >40/min", body)],
[Paragraph("IV", body_bold), Paragraph("<b>Very Severe</b>", warning),
Paragraph("Grade III + AUTONOMIC DYSFUNCTION (labile BP, tachycardia, profuse sweating, hyperpyrexia)", body)],
],
col_widths=[1.2*cm, 2.2*cm, CONTENT_W - 3.4*cm]
),
Spacer(1, 3*mm),
]))
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# ROW 3: MANAGEMENT (full width heading) β 2 columns below
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
story.append(section_header(" MANAGEMENT", HEADER_BG))
story.append(Spacer(1, 2))
# -- LEFT column: Drugs -------------------------------------------------------
lw2 = CONTENT_W * 0.52 - 2*mm
rw2 = CONTENT_W * 0.48 - 2*mm
left2 = [
section_header("A) AIRWAY & VENTILATION", colors.HexColor("#8B0000")),
Spacer(1, 2),
Paragraph("Early <b>intubation / tracheostomy</b> for Grade IIIβIV or laryngospasm", body),
Paragraph("Mechanical ventilation may be needed for <b>weeks</b>", body),
Spacer(1, 3),
section_header("B) CONTROL OF SPASMS", ACCENT_ORG),
Spacer(1, 2),
plain_table(
[Paragraph("<b>Drug</b>", S("h3",fontSize=7,fontName="Helvetica-Bold",textColor=WHITE)),
Paragraph("<b>Dose / Notes</b>", S("h3",fontSize=7,fontName="Helvetica-Bold",textColor=WHITE))],
[
[Paragraph("Diazepam (1st line)", body_bold),
Paragraph("IV; titrated to control spasms", body)],
[Paragraph("Midazolam / Lorazepam", body),
Paragraph("Alternatives to diazepam", body)],
[Paragraph("Magnesium sulphate", body_bold),
Paragraph("IV infusion; controls spasms + autonomic instability", body)],
[Paragraph("Neuromuscular blockade", body),
Paragraph("Vecuronium/pancuronium for refractory spasms (needs ventilator)", body)],
[Paragraph("Baclofen (intrathecal)", body),
Paragraph("Used in severe/refractory cases", body)],
],
col_widths=[lw2*0.42, lw2*0.58]
),
Spacer(1, 3),
section_header("C) PASSIVE IMMUNIZATION β HTIG", MED_BLUE),
Spacer(1, 2),
Paragraph("<b>Human Tetanus Immunoglobulin (HTIG): 3,000β6,000 units IM</b>", body_bold),
Paragraph("Neutralizes UNBOUND toxin only (cannot reverse bound toxin)", body),
Paragraph("Give <b>BEFORE</b> wound debridement", bullet),
Paragraph("Intrathecal HTIG 500 units may be added in severe cases", bullet),
Paragraph("If HTIG unavailable: equine antitoxin (test sensitivity first)", bullet),
Spacer(1, 3),
section_header("D) ANTIBIOTICS", MED_BLUE),
Spacer(1, 2),
plain_table(
[Paragraph("<b>Drug</b>", S("h4",fontSize=7,fontName="Helvetica-Bold",textColor=WHITE)),
Paragraph("<b>Dose</b>", S("h4",fontSize=7,fontName="Helvetica-Bold",textColor=WHITE)),
Paragraph("<b>Note</b>", S("h4",fontSize=7,fontName="Helvetica-Bold",textColor=WHITE))],
[
[Paragraph("Metronidazole (preferred)", body_bold),
Paragraph("500 mg IV/oral q8h x 7β10 days", body),
Paragraph("Preferred over PenG", body)],
[Paragraph("Penicillin G (alt.)", body),
Paragraph("2β4 MU IV q4β6h", body),
Paragraph("GABA antagonist β less preferred", body)],
],
col_widths=[lw2*0.36, lw2*0.36, lw2*0.28]
),
]
# -- RIGHT column: Wound care, autonomic, prophylaxis -------------------------
right2 = [
section_header("E) WOUND MANAGEMENT", colors.HexColor("#4a235a")),
Spacer(1, 2),
Paragraph("Surgical debridement of necrotic tissue & foreign bodies", bullet),
Paragraph("Eliminates anaerobic environment", bullet),
Paragraph("<b>Debride AFTER HTIG is given</b> (debridement releases more toxin)", warning),
Spacer(1, 3),
section_header("F) AUTONOMIC DYSFUNCTION (Grade IV)", ACCENT_RED),
Spacer(1, 2),
plain_table(
[Paragraph("<b>Problem</b>", S("h5",fontSize=7,fontName="Helvetica-Bold",textColor=WHITE)),
Paragraph("<b>Treatment</b>", S("h5",fontSize=7,fontName="Helvetica-Bold",textColor=WHITE))],
[
[Paragraph("Hypertension / Tachycardia", body),
Paragraph("Labetalol IV (alpha+beta) or Magnesium sulphate", body)],
[Paragraph("Bradycardia", body),
Paragraph("Atropine; pacing if severe", body)],
[Paragraph("Hypotension", body),
Paragraph("IV fluids; vasopressors", body)],
[Paragraph("AVOID pure beta-blockers", warning),
Paragraph("Risk of refractory hypotension", body)],
],
col_widths=[rw2*0.45, rw2*0.55]
),
Spacer(1, 3),
section_header("G) WOUND PROPHYLAXIS TABLE", ACCENT_GRN),
Spacer(1, 2),
plain_table(
[Paragraph("<b>Vaccination History</b>", S("h6",fontSize=7,fontName="Helvetica-Bold",textColor=WHITE)),
Paragraph("<b>Clean Minor Wound</b>", S("h6",fontSize=7,fontName="Helvetica-Bold",textColor=WHITE)),
Paragraph("<b>Tetanus-Prone Wound*</b>", S("h6",fontSize=7,fontName="Helvetica-Bold",textColor=WHITE))],
[
[Paragraph("Unknown / <3 doses", body),
Paragraph("<b>TT</b>", body_bold),
Paragraph("<b>TT + HTIG</b>", warning)],
[Paragraph("β₯3 doses; last <5 yr", body),
Paragraph("Nothing", body),
Paragraph("Nothing", body)],
[Paragraph("β₯3 doses; last 5β10 yr", body),
Paragraph("Nothing", body),
Paragraph("<b>TT</b>", body_bold)],
[Paragraph("β₯3 doses; last >10 yr", body),
Paragraph("<b>TT</b>", body_bold),
Paragraph("<b>TT + HTIG</b>", warning)],
],
col_widths=[rw2*0.40, rw2*0.30, rw2*0.30]
),
Spacer(1, 2),
Paragraph("<b>*Tetanus-prone:</b> puncture, contaminated, devitalized, crush, burn, frostbite, >6h old before Rx", body),
Spacer(1, 3),
section_header("H) POST-RECOVERY VACCINATION", MED_BLUE),
Spacer(1, 2),
Paragraph("<b>Tetanus does NOT confer immunity</b> (toxin dose too small)", warning),
Paragraph("Administer 3 doses of Td/TT spaced β₯2 weeks apart after recovery", body),
]
# Assemble 2-col management section
outer_mgmt = Table([[left2, right2]],
colWidths=[lw2 + 2*mm, rw2 + 2*mm])
outer_mgmt.setStyle(TableStyle([
("VALIGN", (0,0), (-1,-1), "TOP"),
("TOPPADDING", (0,0), (-1,-1), 0),
("BOTTOMPADDING",(0,0), (-1,-1), 0),
("LEFTPADDING", (0,0), (-1,-1), 0),
("RIGHTPADDING", (0,0), (-1,-1), 2),
]))
story.append(outer_mgmt)
story.append(Spacer(1, 3*mm))
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# ROW 4: Diagnosis, DDx, Prognosis (full width)
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
diag_prognosis = [
section_header("DIAGNOSIS | DIFFERENTIAL DIAGNOSIS | PROGNOSIS", DARK_BLUE),
Spacer(1, 2),
]
dp_left = [
Paragraph("<b>DIAGNOSIS</b> (clinical β no confirmatory lab test)", body_bold),
Paragraph("Serum anti-tetanus IgG <0.1 IU/mL = not protective", bullet),
Paragraph("Culture from wound does NOT confirm diagnosis", bullet),
Spacer(1, 2),
Paragraph("<b>PROGNOSIS</b>", body_bold),
Paragraph("Hospitalization: 3β6 weeks (Grade IIIβIV)", bullet),
Paragraph("Mortality: <b>20β25%</b> despite optimal management", bullet),
Paragraph("Recovery = axonal terminal sprouting (months)", bullet),
Paragraph("Deaths mostly in patients <b>>55 years</b>", bullet),
]
dp_right = [
Paragraph("<b>DIFFERENTIAL DIAGNOSIS</b>", body_bold),
Paragraph("Strychnine poisoning | Dystonic reaction (metoclopramide, phenothiazines)", bullet),
Paragraph("Neuroleptic malignant syndrome | Serotonin syndrome", bullet),
Paragraph("Stiff-person syndrome | Hypocalcaemic tetany | Peritonitis", bullet),
Spacer(1, 2),
Paragraph("<b>KEY DIFFERENTIATOR:</b> Mental status is normal in tetanus", warning),
]
dp_cols = Table([[dp_left, dp_right]],
colWidths=[CONTENT_W*0.40, CONTENT_W*0.60])
dp_cols.setStyle(TableStyle([
("VALIGN", (0,0),(-1,-1), "TOP"),
("TOPPADDING", (0,0),(-1,-1), 0),
("BOTTOMPADDING",(0,0),(-1,-1), 0),
("LEFTPADDING", (0,0),(-1,-1), 0),
("RIGHTPADDING", (0,0),(-1,-1), 0),
]))
diag_prognosis.append(dp_cols)
story.append(KeepTogether(diag_prognosis))
story.append(Spacer(1, 3*mm))
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# QUICK MEMORY AID
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
story.append(KeepTogether([
section_header("QUICK MEMORY AIDS", colors.HexColor("#4a235a")),
Spacer(1, 2),
plain_table(
[Paragraph("<b>Fact</b>", S("hm",fontSize=7,fontName="Helvetica-Bold",textColor=WHITE)),
Paragraph("<b>Mnemonic / Key Point</b>", S("hm",fontSize=7,fontName="Helvetica-Bold",textColor=WHITE))],
[
[Paragraph("Mechanism", body_bold),
Paragraph("Cuts VAMP-2 β No GABA/glycine β All spasm, no brake", body)],
[Paragraph("First symptom", body_bold),
Paragraph("Trismus (lockjaw) β shortest axon muscles affected first", body)],
[Paragraph("Autonomic storm", body_bold),
Paragraph("Appears in the 2nd week of severe disease", body)],
[Paragraph("Preferred antibiotic", body_bold),
Paragraph("Metronidazole > Penicillin G (PenG is a GABA antagonist like the toxin)", body)],
[Paragraph("Tetanus = immunity?", body_bold),
Paragraph("NO β must vaccinate after recovery (3 doses Td/TT)", body)],
[Paragraph("HTIG timing", body_bold),
Paragraph("Give BEFORE debridement; IM 3000β6000 units", body)],
[Paragraph("Autonomic Rx", body_bold),
Paragraph("Labetalol (both alpha + beta); AVOID pure beta-blockers", body)],
],
col_widths=[CONTENT_W*0.22, CONTENT_W*0.78]
),
Spacer(1, 3*mm),
]))
# ββ FOOTER βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
story.append(HRFlowable(width=CONTENT_W, thickness=0.5, color=BORDER))
story.append(Spacer(1, 1*mm))
story.append(Paragraph(
"Sources: Harrison's Principles of Internal Medicine 22E | Tintinalli's Emergency Medicine | Washington Manual of Medical Therapeutics "
" | For educational use only β verify with current clinical guidelines before patient care.",
footer_style
))
# βββ BUILD ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
doc.build(story)
print(f"PDF saved to: {OUTPUT}")