Create a high-quality medical infographic in a clean MBBS study-note style similar to the reference image. Use a white background with colorful pastel boxes (Red, Blue, Green, Purple, Yellow), hand-drawn medical icons, rounded borders, and bold headings. The layout should be simple, organized, and exam-friendly. At the top, include: Title: "[Nitrates/ Glyceryl trinitrate]" Trick to Remember: Create a short mnemonic (letters, word, or acronym) that helps memorize the drug classes. Then divide the infographic into separate colored sections. For every drug class include: Drug/Class Name Examples (4–6 drugs) Mechanism of Action (Simple arrows ↓ ↑) Main Effects Clinical Uses Important Side Effects Contraindications Special Benefits Important Notes Easy Memory Trick Use icons like: ❤️ Heart 🩸 Blood vessel 🫀 Heart pump 🧠 Brain 🫁 Lungs 💊 Tablet 💉 Injection 💧 Water 🩺 Stethoscope 🧬 Receptor At the bottom include: Quick Recall Box Summarize each class in one line. Use very simple English suitable for MBBS, BDS, Pharm-D, Nursing, and NEET students. Maintain equal spacing, colorful sections, tick marks (✔), arrows (↓ ↑), and a handwritten study-notes appearance. Output should look like a printable A4 medical revision sheet.
ls -la /tmp/workspace/ 2>/dev/null || mkdir -p /tmp/workspace/nitrates-infographic
mkdir -p /tmp/workspace/nitrates-infographic
~/nitrates-infographic/nitrates.py
#!/usr/bin/env python3
"""
Generate a high-quality medical infographic for Nitrates / Glyceryl Trinitrate
using ReportLab - A4 printable study note style.
"""
from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.units import mm, cm
from reportlab.platypus import (
SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
HRFlowable, KeepTogether
)
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_JUSTIFY
from reportlab.platypus import Flowable
from reportlab.lib.colors import HexColor, white, black
from reportlab.pdfgen import canvas
from reportlab.lib.utils import ImageReader
import io
# ── Colour palette ────────────────────────────────────────────────────────────
RED_BG = HexColor("#FFF0F0")
RED_HDR = HexColor("#E53935")
BLUE_BG = HexColor("#F0F4FF")
BLUE_HDR = HexColor("#1565C0")
GREEN_BG = HexColor("#F0FFF4")
GREEN_HDR = HexColor("#2E7D32")
PURPLE_BG = HexColor("#F8F0FF")
PURPLE_HDR = HexColor("#6A1B9A")
YELLOW_BG = HexColor("#FFFDE7")
YELLOW_HDR = HexColor("#F57F17")
TEAL_BG = HexColor("#E0F7FA")
TEAL_HDR = HexColor("#00695C")
ORANGE_BG = HexColor("#FFF3E0")
ORANGE_HDR = HexColor("#E65100")
PINK_BG = HexColor("#FCE4EC")
PINK_HDR = HexColor("#AD1457")
TITLE_BG = HexColor("#1A237E")
TITLE_FG = white
MNEMONIC_BG= HexColor("#FFF8E1")
MNEMONIC_BD= HexColor("#FFB300")
W, H = A4 # 595.27, 841.89 pts
# ── Custom Flowables ──────────────────────────────────────────────────────────
class SectionBox(Flowable):
"""A rounded-rectangle coloured box that wraps a list of Flowables."""
def __init__(self, header_text, header_icon, body_items,
bg_color, hdr_color, width=None, radius=8):
Flowable.__init__(self)
self.header_text = header_text
self.header_icon = header_icon
self.body_items = body_items # list of (label, value) tuples OR plain strings
self.bg_color = bg_color
self.hdr_color = hdr_color
self.width = width or (W - 2*cm)
self.radius = radius
self._height = 0
def wrap(self, availWidth, availHeight):
self.width = availWidth
# estimate height
self._height = 28 + len(self.body_items) * 16 + 10
return (self.width, self._height)
def draw(self):
c = self.canv
w, h = self.width, self._height
r = self.radius
# Background rounded rect
c.setFillColor(self.bg_color)
c.setStrokeColor(self.hdr_color)
c.setLineWidth(1.5)
c.roundRect(0, 0, w, h, r, stroke=1, fill=1)
# Header bar
c.setFillColor(self.hdr_color)
p = c.beginPath()
p.moveTo(r, h)
p.lineTo(w-r, h)
p.arcTo(w-2*r, h-2*r, w, h, 0, 90)
p.lineTo(w, h-24)
p.lineTo(0, h-24)
p.arcTo(0, h-24, 2*r, h, 90, 90) # top-left arc
p.close()
# simpler: just a rect for header
c.roundRect(0, h-26, w, 26, r, stroke=0, fill=1)
# patch corners at bottom of header
c.rect(0, h-26, w, 13, stroke=0, fill=1)
# Header text
c.setFillColor(white)
c.setFont("Helvetica-Bold", 10)
c.drawString(8, h-18, f"{self.header_icon} {self.header_text}")
# Body rows
y = h - 38
for item in self.body_items:
if isinstance(item, tuple):
label, value = item
c.setFillColor(self.hdr_color)
c.setFont("Helvetica-Bold", 8)
c.drawString(10, y, label)
c.setFillColor(HexColor("#222222"))
c.setFont("Helvetica", 8)
# wrap value
max_w = w - 14
lines = self._wrap_text(str(value), "Helvetica", 8, max_w - 80)
c.drawString(90, y, lines[0] if lines else "")
for i, extra in enumerate(lines[1:], 1):
y -= 11
c.drawString(90, y, extra)
else:
c.setFillColor(HexColor("#333333"))
c.setFont("Helvetica", 8)
lines = self._wrap_text(str(item), "Helvetica", 8, w - 20)
c.drawString(10, y, lines[0] if lines else "")
for extra in lines[1:]:
y -= 11
c.drawString(10, y, extra)
y -= 14
if y < 8:
break
def _wrap_text(self, text, font, size, maxw):
words = text.split()
lines, cur = [], ""
from reportlab.pdfbase.pdfmetrics import stringWidth
for w in words:
test = (cur + " " + w).strip()
if stringWidth(test, font, size) <= maxw:
cur = test
else:
if cur:
lines.append(cur)
cur = w
if cur:
lines.append(cur)
return lines or [""]
# ── Build the PDF ─────────────────────────────────────────────────────────────
def build_pdf(path):
doc = SimpleDocTemplate(
path,
pagesize=A4,
rightMargin=1.2*cm,
leftMargin=1.2*cm,
topMargin=1.2*cm,
bottomMargin=1.2*cm,
)
styles = getSampleStyleSheet()
def ps(name, **kw):
return ParagraphStyle(name, parent=styles["Normal"], **kw)
title_style = ps("Title2",
fontSize=20, textColor=white, fontName="Helvetica-Bold",
alignment=TA_CENTER, spaceAfter=4, leading=24)
subtitle_style = ps("Subtitle2",
fontSize=11, textColor=HexColor("#BBDEFB"), fontName="Helvetica-Oblique",
alignment=TA_CENTER, spaceAfter=2, leading=14)
mnem_label = ps("MnemLabel",
fontSize=10, textColor=HexColor("#5D4037"), fontName="Helvetica-Bold",
alignment=TA_CENTER, spaceAfter=2)
mnem_text = ps("MnemText",
fontSize=13, textColor=HexColor("#E65100"), fontName="Helvetica-Bold",
alignment=TA_CENTER, spaceAfter=1, leading=18)
mnem_expand = ps("MnemExpand",
fontSize=9, textColor=HexColor("#333333"), fontName="Helvetica",
alignment=TA_CENTER, spaceAfter=2, leading=12)
section_hdr = ps("SHdr",
fontSize=11, textColor=white, fontName="Helvetica-Bold",
alignment=TA_LEFT, leading=14)
body_bold = ps("BB",
fontSize=9, textColor=HexColor("#1A237E"), fontName="Helvetica-Bold",
leading=13)
body_norm = ps("BN",
fontSize=9, textColor=HexColor("#222222"), fontName="Helvetica",
leading=12)
small_note = ps("SN",
fontSize=8, textColor=HexColor("#444444"), fontName="Helvetica-Oblique",
leading=11)
recall_hdr = ps("RecallHdr",
fontSize=11, textColor=white, fontName="Helvetica-Bold",
alignment=TA_CENTER, leading=14)
recall_item = ps("RecallItem",
fontSize=9, textColor=HexColor("#1B5E20"), fontName="Helvetica",
leading=13, leftIndent=6)
content_width = W - 2.4*cm
# ── Helper: coloured cell paragraph ──────────────────────────────────────
def cp(text, style, bg=None):
return Paragraph(text, style)
def hdr_para(icon, text, color):
return Paragraph(f'<font color="white"><b>{icon} {text}</b></font>',
ps("hp", fontSize=10, fontName="Helvetica-Bold",
textColor=white, leading=13))
def bullet(text, tick=True):
mark = "✔ " if tick else "→ "
return Paragraph(f"{mark}{text}", body_norm)
def arrow_item(label, val):
return Paragraph(f'<b>{label}:</b> {val}', body_norm)
# ════════════════════════════════════════════════════════════════════════
# TITLE BLOCK
# ════════════════════════════════════════════════════════════════════════
story = []
# Title table (dark blue header)
title_data = [[
Paragraph(
'<font color="white"><b>💊 NITRATES / GLYCERYL TRINITRATE (GTN)</b></font>',
ps("T", fontSize=18, fontName="Helvetica-Bold", textColor=white,
alignment=TA_CENTER, leading=22)
)
]]
title_tbl = Table(title_data, colWidths=[content_width])
title_tbl.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), TITLE_BG),
("ROUNDEDCORNERS", [8]),
("TOPPADDING", (0,0), (-1,-1), 10),
("BOTTOMPADDING",(0,0), (-1,-1), 10),
("LEFTPADDING", (0,0), (-1,-1), 12),
("RIGHTPADDING", (0,0), (-1,-1), 12),
]))
story.append(title_tbl)
story.append(Spacer(1, 4))
# Subtitle row
subtitle_data = [[
Paragraph(
'<font color="#1565C0"><i>Organic Nitrate Vasodilator | Anti-Anginal | Smooth Muscle Relaxant</i></font>',
ps("sub", fontSize=9, fontName="Helvetica-Oblique",
textColor=HexColor("#1565C0"), alignment=TA_CENTER)
),
Paragraph(
'<font color="#444444">📚 For MBBS · BDS · Pharm-D · Nursing · NEET</font>',
ps("sub2", fontSize=9, fontName="Helvetica",
textColor=HexColor("#444444"), alignment=TA_CENTER)
)
]]
sub_tbl = Table(subtitle_data, colWidths=[content_width*0.6, content_width*0.4])
sub_tbl.setStyle(TableStyle([
("TOPPADDING", (0,0),(-1,-1), 2),
("BOTTOMPADDING",(0,0),(-1,-1), 2),
]))
story.append(sub_tbl)
story.append(Spacer(1, 6))
# ════════════════════════════════════════════════════════════════════════
# MNEMONIC BOX
# ════════════════════════════════════════════════════════════════════════
mnem_data = [[
Paragraph('🧠 <b>TRICK TO REMEMBER</b>', ps("ml", fontSize=10,
fontName="Helvetica-Bold", textColor=HexColor("#5D4037"), alignment=TA_CENTER)),
Paragraph(
'<b><font color="#E65100" size="14">"VEIN-GO"</font></b>',
ps("mv", fontSize=12, fontName="Helvetica-Bold",
textColor=HexColor("#E65100"), alignment=TA_CENTER)
),
Paragraph(
'<b>V</b>eno-dilation → ↓ Preload | '
'<b>E</b>ndogenous NO donor | '
'<b>I</b>schaemia relief | '
'<b>N</b>itroglycerin first line | '
'<b>G</b>uanylate cyclase ↑ | '
'<b>O</b>vernight patch-free interval',
ps("me", fontSize=8, fontName="Helvetica",
textColor=HexColor("#333333"), alignment=TA_CENTER, leading=11)
)
]]
mnem_tbl = Table(mnem_data, colWidths=[content_width*0.22, content_width*0.2, content_width*0.58])
mnem_tbl.setStyle(TableStyle([
("BACKGROUND", (0,0),(-1,-1), MNEMONIC_BG),
("ROUNDEDCORNERS",[8]),
("BOX", (0,0),(-1,-1), 1.5, MNEMONIC_BD),
("TOPPADDING", (0,0),(-1,-1), 8),
("BOTTOMPADDING",(0,0),(-1,-1), 8),
("LEFTPADDING", (0,0),(-1,-1), 6),
("RIGHTPADDING", (0,0),(-1,-1), 6),
("VALIGN", (0,0),(-1,-1), "MIDDLE"),
]))
story.append(mnem_tbl)
story.append(Spacer(1, 7))
# ════════════════════════════════════════════════════════════════════════
# HELPER: make a section block as a Table
# ════════════════════════════════════════════════════════════════════════
def make_section(icon, title, rows, bg, hdr, width=None):
"""rows = list of (label_para, value_para) or single para"""
w = width or content_width
header_row = [[
Paragraph(f'<font color="white"><b>{icon} {title}</b></font>',
ps("sh", fontSize=10, fontName="Helvetica-Bold",
textColor=white, leading=13))
]]
body_rows = []
for row in rows:
if isinstance(row, (list, tuple)) and len(row) == 2:
body_rows.append([row[0], row[1]])
else:
body_rows.append([row, ""])
all_rows = header_row + body_rows
col_widths = [w*0.32, w*0.68] if body_rows and len(body_rows[0]) == 2 else [w]
# If header is full width
tbl_data = [header_row[0]] + body_rows
# build as merged header + 2-col body
flat = [[Paragraph(f'<font color="white"><b>{icon} {title}</b></font>',
ps("sh2", fontSize=10, fontName="Helvetica-Bold",
textColor=white, leading=13)),""]
] + body_rows
tbl = Table(flat, colWidths=[w*0.30, w*0.70])
style = [
("BACKGROUND", (0,0),(-1,0), hdr),
("SPAN", (0,0),(-1,0)),
("BACKGROUND", (0,1),(-1,-1), bg),
("ROUNDEDCORNERS",[6]),
("BOX", (0,0),(-1,-1), 1.2, hdr),
("GRID", (0,1),(-1,-1), 0.3, HexColor("#DDDDDD")),
("TOPPADDING", (0,0),(-1,-1), 5),
("BOTTOMPADDING",(0,0),(-1,-1), 5),
("LEFTPADDING", (0,0),(-1,-1), 7),
("RIGHTPADDING", (0,0),(-1,-1), 7),
("VALIGN", (0,0),(-1,-1), "TOP"),
("FONTNAME", (0,1),(0,-1), "Helvetica-Bold"),
("FONTSIZE", (0,1),(0,-1), 8),
("TEXTCOLOR", (0,1),(0,-1), hdr),
("FONTNAME", (1,1),(1,-1), "Helvetica"),
("FONTSIZE", (1,1),(1,-1), 8.5),
]
tbl.setStyle(TableStyle(style))
return tbl
def lbl(t, c=None):
color = c or HexColor("#1A237E")
return Paragraph(f'<b><font color="{color.hexval() if hasattr(c,"hexval") else "#1A237E"}">{t}</font></b>',
ps("lbl", fontSize=8.5, fontName="Helvetica-Bold",
textColor=HexColor("#1A237E"), leading=12))
def val(t):
return Paragraph(t, ps("val", fontSize=8.5, fontName="Helvetica",
textColor=HexColor("#222222"), leading=12))
def lbl2(t, col):
return Paragraph(f'<b>{t}</b>',
ps("lbl2"+t, fontSize=8.5, fontName="Helvetica-Bold",
textColor=col, leading=12))
# ════════════════════════════════════════════════════════════════════════
# SECTION 1 – DRUG / CLASS NAME + EXAMPLES (RED)
# ════════════════════════════════════════════════════════════════════════
s1 = make_section(
"💊", "DRUG CLASS & EXAMPLES",
[
(lbl2("Drug Class:", RED_HDR), val("Organic Nitrates (Nitrovasodilators)")),
(lbl2("Prototype:", RED_HDR), val("Glyceryl Trinitrate (GTN / Nitroglycerin)")),
(lbl2("Short-acting:", RED_HDR), val("✔ GTN (sublingual tab/spray) — onset 1–3 min\n✔ Amyl nitrite (inhalation)")),
(lbl2("Long-acting:", RED_HDR), val("✔ Isosorbide Dinitrate (ISDN) — oral/sublingual\n✔ Isosorbide Mononitrate (ISMN) — oral\n✔ GTN patch / ointment (transdermal)\n✔ Pentaerythritol tetranitrate (PETN)")),
],
RED_BG, RED_HDR
)
story.append(s1)
story.append(Spacer(1, 6))
# ════════════════════════════════════════════════════════════════════════
# SECTION 2 – MECHANISM OF ACTION (BLUE)
# ════════════════════════════════════════════════════════════════════════
s2 = make_section(
"🧬", "MECHANISM OF ACTION",
[
(lbl2("Step 1:", BLUE_HDR), val("GTN (prodrug) → metabolised by mitochondrial aldehyde dehydrogenase-2 (ALDH-2) → releases Nitric Oxide (NO)")),
(lbl2("Step 2:", BLUE_HDR), val("NO → activates soluble Guanylate Cyclase (sGC) → ↑ cGMP")),
(lbl2("Step 3:", BLUE_HDR), val("↑ cGMP → activates Protein Kinase G (PKG) → dephosphorylates myosin light chain (MLC)")),
(lbl2("Step 4:", BLUE_HDR), val("↓ intracellular Ca²⁺ + dephosphorylated MLC → Vascular Smooth Muscle RELAXATION → Vasodilation")),
(lbl2("Net Effect:", BLUE_HDR), val("Veins >> Arteries dilated (at low doses) | Both dilated at high doses")),
(lbl2("Also:", BLUE_HDR), val("↑ cGMP → inhibits platelet aggregation | Bronchial & GI smooth muscle relaxation")),
],
BLUE_BG, BLUE_HDR
)
story.append(s2)
story.append(Spacer(1, 6))
# ════════════════════════════════════════════════════════════════════════
# ROW of 2: MAIN EFFECTS (GREEN) + HAEMODYNAMIC (TEAL)
# ════════════════════════════════════════════════════════════════════════
hw = (content_width - 5) / 2
def make_section_half(icon, title, rows, bg, hdr):
return make_section(icon, title, rows, bg, hdr, width=hw)
s3a = make_section_half(
"❤️", "MAIN EFFECTS",
[
(lbl2("Venous:", GREEN_HDR), val("↑ venous capacitance → ↓ venous return → ↓ Preload")),
(lbl2("Arterial:", GREEN_HDR), val("↓ peripheral resistance → ↓ Afterload (high dose)")),
(lbl2("Coronary:", GREEN_HDR), val("Dilates large coronary arteries → relieves vasospasm")),
(lbl2("O₂ demand:", GREEN_HDR),val("↓ LVEDV → ↓ wall tension → ↓ MVO₂")),
(lbl2("Collateral:", GREEN_HDR),val("↑ collateral blood flow to ischaemic subendocardium")),
],
GREEN_BG, GREEN_HDR
)
s3b = make_section_half(
"🩸", "HAEMODYNAMIC CHANGES",
[
(lbl2("HR:", TEAL_HDR), val("↑ (reflex tachycardia)")),
(lbl2("BP:", TEAL_HDR), val("↓ Systolic & Diastolic")),
(lbl2("Preload:", TEAL_HDR), val("↓↓ (main effect)")),
(lbl2("Afterload:", TEAL_HDR), val("↓ (at higher doses)")),
(lbl2("LVEDP:", TEAL_HDR), val("↓ (reduces pulmonary congestion)")),
(lbl2("CO:", TEAL_HDR), val("↔ / slight ↑ in heart failure")),
],
TEAL_BG, TEAL_HDR
)
row1 = Table([[s3a, s3b]], colWidths=[hw, hw])
row1.setStyle(TableStyle([
("TOPPADDING", (0,0),(-1,-1), 0),
("BOTTOMPADDING", (0,0),(-1,-1), 0),
("LEFTPADDING", (0,0),(-1,-1), 0),
("RIGHTPADDING", (0,0),(0,-1), 3),
("RIGHTPADDING", (1,0),(1,-1), 0),
]))
story.append(row1)
story.append(Spacer(1, 6))
# ════════════════════════════════════════════════════════════════════════
# SECTION 4 – CLINICAL USES (PURPLE)
# ════════════════════════════════════════════════════════════════════════
s4 = make_section(
"🩺", "CLINICAL USES",
[
(lbl2("1.", PURPLE_HDR), val("✔ Acute Angina Attack — sublingual GTN (drug of choice for immediate relief)")),
(lbl2("2.", PURPLE_HDR), val("✔ Stable Angina — prophylaxis before exertion (ISDN / ISMN long-acting)")),
(lbl2("3.", PURPLE_HDR), val("✔ Unstable Angina — IV nitroglycerin")),
(lbl2("4.", PURPLE_HDR), val("✔ Variant (Prinzmetal) Angina — relieves coronary vasospasm")),
(lbl2("5.", PURPLE_HDR), val("✔ Acute Heart Failure / Pulmonary Oedema — ↓ preload → ↓ pulmonary congestion")),
(lbl2("6.", PURPLE_HDR), val("✔ Hypertensive Emergency — IV GTN")),
(lbl2("7.", PURPLE_HDR), val("✔ Acute MI — used with other agents")),
(lbl2("8.", PURPLE_HDR), val("✔ Esophageal spasm, Anal fissure (topical GTN ointment)")),
],
PURPLE_BG, PURPLE_HDR
)
story.append(s4)
story.append(Spacer(1, 6))
# ════════════════════════════════════════════════════════════════════════
# ROW of 2: SIDE EFFECTS (ORANGE) + CONTRAINDICATIONS (RED-dark)
# ════════════════════════════════════════════════════════════════════════
s5a = make_section_half(
"⚠️", "SIDE EFFECTS",
[
(lbl2("Common:", ORANGE_HDR), val("Throbbing headache (most common!) — due to meningeal vasodilation")),
(lbl2("Common:", ORANGE_HDR), val("Postural hypotension, Dizziness, Flushing of face")),
(lbl2("Reflex:", ORANGE_HDR), val("Tachycardia (baroreceptor reflex)")),
(lbl2("Serious:", ORANGE_HDR), val("Syncope (especially if upright)")),
(lbl2("Tolerance:", ORANGE_HDR), val("Develops rapidly with continuous use (nitrate tolerance)")),
(lbl2("Methaemoglobinaemia:", ORANGE_HDR), val("Rare, with excessive doses")),
],
ORANGE_BG, ORANGE_HDR
)
s5b = make_section_half(
"🚫", "CONTRAINDICATIONS",
[
(lbl2("Absolute:", RED_HDR), val("↔ PDE-5 inhibitors (sildenafil, tadalafil) — risk of fatal hypotension")),
(lbl2("Absolute:", RED_HDR), val("Severe hypotension (SBP < 90 mmHg)")),
(lbl2("Absolute:", RED_HDR), val("Hypovolaemia / Shock")),
(lbl2("Caution:", RED_HDR), val("Raised intracranial pressure (ICP)")),
(lbl2("Caution:", RED_HDR), val("Hypertrophic obstructive cardiomyopathy (HOCM)")),
(lbl2("Caution:", RED_HDR), val("Right ventricular MI (dependent on preload)")),
(lbl2("Caution:", RED_HDR), val("Glaucoma (closed angle) — can ↑ IOP")),
],
RED_BG, RED_HDR
)
row2 = Table([[s5a, s5b]], colWidths=[hw, hw])
row2.setStyle(TableStyle([
("TOPPADDING", (0,0),(-1,-1), 0),
("BOTTOMPADDING", (0,0),(-1,-1), 0),
("LEFTPADDING", (0,0),(-1,-1), 0),
("RIGHTPADDING", (0,0),(0,-1), 3),
("RIGHTPADDING", (1,0),(1,-1), 0),
]))
story.append(row2)
story.append(Spacer(1, 6))
# ════════════════════════════════════════════════════════════════════════
# ROW of 2: SPECIAL BENEFITS (YELLOW) + TOLERANCE (PINK)
# ════════════════════════════════════════════════════════════════════════
s6a = make_section_half(
"⭐", "SPECIAL BENEFITS & NOTES",
[
(lbl2("★ GTN:", YELLOW_HDR), val("Acts within 1–3 min SL — fastest antianginal")),
(lbl2("★ Route:", YELLOW_HDR), val("SL / Spray — bypasses first-pass metabolism")),
(lbl2("★ ISMN:", YELLOW_HDR), val("No first-pass metabolism → 100% oral bioavailability")),
(lbl2("★ Patch:", YELLOW_HDR), val("12 h on / 12 h off → prevents tolerance")),
(lbl2("★ HF use:", YELLOW_HDR), val("↓ preload → relieves pulmonary oedema rapidly")),
(lbl2("★ Platelet:", YELLOW_HDR),val("↑ cGMP → mild antiplatelet effect")),
],
YELLOW_BG, YELLOW_HDR
)
s6b = make_section_half(
"🔄", "TOLERANCE & HOW TO PREVENT",
[
(lbl2("Cause:", PINK_HDR), val("Continuous use → generates ROS → inhibits ALDH-2 → ↓ NO production")),
(lbl2("Also:", PINK_HDR), val("↑ sympathetic activation, ↑ plasma volume, neurohormonal activation")),
(lbl2("Prevention:", PINK_HDR), val("✔ Provide 10–12 h nitrate-free interval (overnight) daily")),
(lbl2("Patch:", PINK_HDR), val("Remove patch at night, reapply in morning")),
(lbl2("ISMN:", PINK_HDR), val("Asymmetric dosing: 8 AM + 3 PM (not evenly spaced)")),
(lbl2("⚠ Note:", PINK_HDR), val("Variant angina may worsen at night → monitor during free interval")),
],
PINK_BG, PINK_HDR
)
row3 = Table([[s6a, s6b]], colWidths=[hw, hw])
row3.setStyle(TableStyle([
("TOPPADDING", (0,0),(-1,-1), 0),
("BOTTOMPADDING", (0,0),(-1,-1), 0),
("LEFTPADDING", (0,0),(-1,-1), 0),
("RIGHTPADDING", (0,0),(0,-1), 3),
("RIGHTPADDING", (1,0),(1,-1), 0),
]))
story.append(row3)
story.append(Spacer(1, 6))
# ════════════════════════════════════════════════════════════════════════
# PHARMACOKINETICS (BLUE-light)
# ════════════════════════════════════════════════════════════════════════
s7 = make_section(
"⏱️", "PHARMACOKINETICS AT A GLANCE",
[
(lbl2("GTN SL:", BLUE_HDR), val("Onset: 1–3 min | Duration: 20–30 min | Route: Sublingual/Spray/Patch/IV/Topical")),
(lbl2("ISDN oral:", BLUE_HDR), val("Onset: 30–60 min | Duration: 4–6 h | Significant first-pass metabolism")),
(lbl2("ISMN oral:", BLUE_HDR), val("Onset: 30–60 min | Duration: 6–8 h | NO first-pass loss → bioavailability ~100%")),
(lbl2("GTN patch:", BLUE_HDR), val("Onset: 30–60 min | Duration: 12–16 h (worn 12 h then removed)")),
(lbl2("Metabolism:", BLUE_HDR), val("Liver (hepatic conjugation + ALDH-2) → inactive glucuronide metabolites")),
(lbl2("Half-life:", BLUE_HDR), val("GTN: 1–3 min | ISDN: 45 min | ISMN: 4–5 h")),
],
BLUE_BG, BLUE_HDR
)
story.append(s7)
story.append(Spacer(1, 6))
# ════════════════════════════════════════════════════════════════════════
# IMPORTANT EXAM NOTES (PURPLE-light)
# ════════════════════════════════════════════════════════════════════════
s8 = make_section(
"📝", "IMPORTANT EXAM NOTES",
[
(lbl2("★ Note 1:", PURPLE_HDR), val("GTN is NOT a true 'nitro' compound — it is an ESTER of glycerol and nitric acid, but officially called nitroglycerin (erroneously)")),
(lbl2("★ Note 2:", PURPLE_HDR), val("Main antianginal benefit = ↓ PRELOAD (not primarily coronary dilation) — proven by direct intracoronary injection studies")),
(lbl2("★ Note 3:", PURPLE_HDR), val("PDE-5 inhibitors (sildenafil, tadalafil, vardenafil) + Nitrates = ABSOLUTELY CONTRAINDICATED → both ↑ cGMP → severe, fatal hypotension")),
(lbl2("★ Note 4:", PURPLE_HDR), val("Headache due to GTN is a therapeutic end-point in older texts (proves absorption) but is a side effect to manage clinically")),
(lbl2("★ Note 5:", PURPLE_HDR), val("Alfred Nobel himself (inventor of dynamite/nitroglycerin) was prescribed GTN for his angina in 1890 — he found this ironic")),
(lbl2("★ Note 6:", PURPLE_HDR), val("1998 Nobel Prize in Medicine awarded for discovery of NO as signalling molecule (Furchgott, Ignarro, Murad)")),
],
PURPLE_BG, PURPLE_HDR
)
story.append(s8)
story.append(Spacer(1, 6))
# ════════════════════════════════════════════════════════════════════════
# EASY MEMORY TRICK (YELLOW bold)
# ════════════════════════════════════════════════════════════════════════
trick_data = [[
Paragraph('🧠 <b>EASY MEMORY TRICK</b>',
ps("th", fontSize=10, fontName="Helvetica-Bold", textColor=white, alignment=TA_CENTER)),
]]
trick_hdr = Table(trick_data, colWidths=[content_width])
trick_hdr.setStyle(TableStyle([
("BACKGROUND", (0,0),(-1,-1), YELLOW_HDR),
("ROUNDEDCORNERS",[6]),
("TOPPADDING", (0,0),(-1,-1), 6),
("BOTTOMPADDING",(0,0),(-1,-1), 6),
]))
story.append(trick_hdr)
tricks = [
('💡 "VEIN GO" mnemonic:', '"V-E-I-N-G-O" — Venodilation, Endogenous NO, Ischaemia relief, Nitroglycerin first, Guanylate cyclase, Overnight patch-free'),
('💡 Headache = GTN:', '"Nitrates NAIL the HEAD" — N=Nitroglycerin, A=Acts fast, I=Ischaemia, L=Lowers preload, HEAD = headache side effect'),
('💡 Contraindication:', '"NO + VIGRA = DANGER" — Nitrates + Viagra (PDE5i) = NO cGMP overflow = fatal drop in BP'),
('💡 Tolerance trick:', '"Night Off for Nitrates" — remove patch at night to prevent tolerance'),
('💡 Bioavailability:', '"I-S-M-N = I Shall Make No excuses" — Isosorbide Mononitrate has NO first-pass metabolism'),
]
trick_body_data = [[
Paragraph(f'<b>{t[0]}</b>', ps("tk", fontSize=8.5, fontName="Helvetica-Bold",
textColor=YELLOW_HDR, leading=12)),
Paragraph(t[1], ps("tv", fontSize=8.5, fontName="Helvetica",
textColor=HexColor("#333333"), leading=12))
] for t in tricks]
trick_body = Table(trick_body_data, colWidths=[content_width*0.28, content_width*0.72])
trick_body.setStyle(TableStyle([
("BACKGROUND", (0,0),(-1,-1), YELLOW_BG),
("BOX", (0,0),(-1,-1), 1.2, YELLOW_HDR),
("GRID", (0,0),(-1,-1), 0.3, HexColor("#FFE082")),
("TOPPADDING", (0,0),(-1,-1), 5),
("BOTTOMPADDING",(0,0),(-1,-1), 5),
("LEFTPADDING", (0,0),(-1,-1), 7),
("RIGHTPADDING", (0,0),(-1,-1), 7),
("VALIGN", (0,0),(-1,-1), "TOP"),
]))
story.append(trick_body)
story.append(Spacer(1, 6))
# ════════════════════════════════════════════════════════════════════════
# QUICK RECALL BOX (GREEN)
# ════════════════════════════════════════════════════════════════════════
qr_data = [[
Paragraph('⚡ <b>QUICK RECALL BOX — One Line Per Key Point</b>',
ps("qh", fontSize=10, fontName="Helvetica-Bold", textColor=white, alignment=TA_CENTER)),
]]
qr_hdr = Table(qr_data, colWidths=[content_width])
qr_hdr.setStyle(TableStyle([
("BACKGROUND", (0,0),(-1,-1), GREEN_HDR),
("ROUNDEDCORNERS",[6]),
("TOPPADDING", (0,0),(-1,-1), 7),
("BOTTOMPADDING",(0,0),(-1,-1), 7),
]))
story.append(qr_hdr)
recalls = [
("MOA", "NO → ↑ cGMP → ↓ Ca²⁺ → Vascular smooth muscle relaxation → Vasodilation"),
("Best route", "Sublingual GTN for ACUTE attack (onset 1–3 min, bypasses first-pass)"),
("Main use", "Stable angina, Unstable angina, Variant angina, Acute HF, HTN emergency"),
("Key SE", "Headache (most common), Hypotension, Reflex tachycardia, Tolerance"),
("Tolerance", "Prevent by 10–12 h nitrate-free interval (remove patch overnight)"),
("Big contraind","PDE-5 inhibitors (sildenafil) — causes FATAL hypotension"),
("ISMN benefit", "100% oral bioavailability — no first-pass effect"),
("ISDN", "Prodrug → active metabolites (IS-2-MN, IS-5-MN); dose twice daily"),
("Preload ↓", "Major antianginal mechanism (not coronary dilation as primary effect)"),
("HF use", "Nitrates + Hydralazine = A-HeFT regimen (alternative to ACEi in Black patients with HFrEF)"),
]
recall_rows = [[
Paragraph(f'<b><font color="{GREEN_HDR.hexval()}">{r[0]}</font></b>',
ps("rlbl", fontSize=8.5, fontName="Helvetica-Bold",
textColor=GREEN_HDR, leading=12)),
Paragraph(f"→ {r[1]}",
ps("rval", fontSize=8.5, fontName="Helvetica",
textColor=HexColor("#1B5E20"), leading=12))
] for r in recalls]
recall_tbl = Table(recall_rows, colWidths=[content_width*0.18, content_width*0.82])
recall_tbl.setStyle(TableStyle([
("BACKGROUND", (0,0),(-1,-1), GREEN_BG),
("BOX", (0,0),(-1,-1), 1.2, GREEN_HDR),
("GRID", (0,0),(-1,-1), 0.3, HexColor("#A5D6A7")),
("TOPPADDING", (0,0),(-1,-1), 5),
("BOTTOMPADDING",(0,0),(-1,-1), 5),
("LEFTPADDING", (0,0),(-1,-1), 7),
("RIGHTPADDING", (0,0),(-1,-1), 7),
("VALIGN", (0,0),(-1,-1), "MIDDLE"),
]))
story.append(recall_tbl)
story.append(Spacer(1, 6))
# ════════════════════════════════════════════════════════════════════════
# FOOTER
# ════════════════════════════════════════════════════════════════════════
footer_data = [[
Paragraph(
'📚 Sources: Goodman & Gilman\'s Pharmacology 14e | Katzung Basic & Clinical Pharmacology 16e | Lippincott Pharmacology 8e',
ps("ft", fontSize=7.5, fontName="Helvetica-Oblique",
textColor=HexColor("#666666"), alignment=TA_CENTER)
),
Paragraph(
'🎓 For MBBS · BDS · Pharm-D · Nursing · NEET · USMLE',
ps("ft2", fontSize=7.5, fontName="Helvetica-Bold",
textColor=HexColor("#1A237E"), alignment=TA_CENTER)
)
]]
footer_tbl = Table(footer_data, colWidths=[content_width*0.65, content_width*0.35])
footer_tbl.setStyle(TableStyle([
("TOPPADDING", (0,0),(-1,-1), 4),
("BOTTOMPADDING",(0,0),(-1,-1), 4),
("LINEABOVE", (0,0),(-1,0), 0.5, HexColor("#AAAAAA")),
]))
story.append(footer_tbl)
doc.build(story)
print(f"PDF saved to {path}")
if __name__ == "__main__":
out = "/tmp/workspace/nitrates-infographic/Nitrates_GTN_Infographic.pdf"
build_pdf(out)
cd /tmp/workspace/nitrates-infographic && python nitrates.py 2>&1
A clean, printable A4 medical study note infographic titled "NITRATES / GLYCERYL TRINITRATE (GTN)" in bold white text on a dark navy blue header bar. Below the title a yellow mnemonic box labeled "TRICK TO REMEMBER: VEIN-GO" with the letters expanded. The infographic has 8 colorful pastel sections with rounded corners: 1. RED section "DRUG CLASS & EXAMPLES" with icons, listing GTN, ISDN, ISMN, amyl nitrite, PETN 2. BLUE section "MECHANISM OF ACTION" with arrows showing: GTN → NO → ↑cGMP → ↓MLC phosphorylation → VSM relaxation 3. GREEN section "MAIN EFFECTS" and TEAL section "HAEMODYNAMIC CHANGES" side by side 4. PURPLE section "CLINICAL USES" with tick marks: angina, heart failure, hypertensive emergency, esophageal spasm 5. ORANGE section "SIDE EFFECTS" and RED section "CONTRAINDICATIONS" (PDE5 inhibitors = DANGER) side by side 6. YELLOW section "SPECIAL BENEFITS & NOTES" 7. PINK section "TOLERANCE & PREVENTION" (12h patch-free interval) 8. GREEN "QUICK RECALL BOX" at bottom summarizing each point in one line Uses icons: heart, blood vessel, brain, lungs, tablet, injection, stethoscope, receptor. White background. Handwritten study-notes appearance. Bold headings, arrows (↓↑), tick marks (✔). Suitable for MBBS/NEET/Pharm-D students. A4 printable revision sheet style.

Nitrates GTN Infographic
PDF Document · PDF
| Color | Section |
|---|---|
| 🔴 Red | Drug Class & Examples (GTN, ISDN, ISMN, PETN, Amyl Nitrite) |
| 🔵 Blue | Mechanism of Action (NO → ↑cGMP → ↓Ca²⁺ → VSM Relaxation) |
| 🟢 Green | Main Effects + Haemodynamic Changes |
| 🟣 Purple | Clinical Uses (8 indications with ✔ marks) |
| 🟠 Orange + Red | Side Effects & Contraindications |
| 🟡 Yellow | Special Benefits & Exam Notes |
| 🩷 Pink | Tolerance & How to Prevent (12 h nitrate-free interval) |
| 💚 Green | Quick Recall Box (10 one-liners) |