LARYNGOSPASM Definition • Laryngospasm is an exaggerated protective glottic closure reflex causing forceful closure of the vocal cords that persists even after the stimulus is removed, leading to partial or complete upper airway obstruction. ⸻ Etiology 1. Patient Factors • Pediatric age group (most common) • Upper respiratory tract infection (URTI) • Chronic smokers • Reactive airway disease 2. Surgery-related Factors • ENT surgeries (tonsillectomy, adenoidectomy) • Airway procedures • Endoscopy • Esophagoscopy • Upper abdominal surgery • Pelvic, thoracic and abdominal visceral stimulation 3. Anesthetic Factors Light plane of anesthesia • Most common intraoperative cause Drugs • Morphine • Barbiturates (increase parasympathetic activity) Volatile anesthetics • Ether > Isoflurane > Halothane > Enflurane • Sevoflurane is least irritant Airway irritation • Secretions • Blood • Vomitus • Suctioning • Nasogastric tube • Oropharyngeal/nasopharyngeal airway • Nasal irritation ⸻ Postoperative Causes Immediate • Extubation during light anesthesia • Especially after halothane anesthesia Delayed • Secretions around vocal cords • Regurgitated gastric contents • Silent aspiration beside NG tube • Oropharyngeal airway irritation • Hypoxia causing vomiting • Epiglottitis ⸻ Pathophysiology Stimulus → Superior laryngeal nerve → Vagus nerve → Reflex contraction of laryngeal adductor muscles → Complete or partial glottic closure → Airway obstruction ↓ Hypoxia + Hypercarbia ↓ Negative intrathoracic pressure ↓ Pulmonary edema (Negative Pressure Pulmonary Edema) ⸻ Clinical Features Early Signs • Inspiratory stridor • Dyspnea • Tachypnea • Anxiety • Use of accessory muscles Progressive Obstruction • Intercostal retractions • Suprasternal indrawing • Tracheal tug • Paradoxical chest and abdominal movements • Chest sinks while abdomen rises during inspiration Complete Laryngospasm • Silent chest • No air entry • Rapid desaturation • Cyanosis • Bradycardia (especially children) • Cardiac arrest if untreated ⸻ Diagnosis Clinical diagnosis based on: • Sudden airway obstruction • No effective ventilation • Stridor or silent chest • Falling SpO₂ • Increased inspiratory effort ⸻ Management Step 1: Call for Help • Stop surgery • Inform surgeon • 100% oxygen ⸻ Step 2: Remove Stimulus • Stop surgical stimulation • Suction blood/secretions • Remove airway irritants • Remove nasogastric tube if indicated ⸻ Step 3: Deepen Anesthesia Propofol • 1–2 mg/kg IV • Drug of choice Volatile anesthetic • Sevoflurane preferred • Avoid irritant agents ⸻ Step 4: Open Airway • Head extension • Jaw thrust • Chin lift Larson’s Maneuver • Bilateral pressure behind angle of mandible just anterior to mastoid process • Stimulates painful point and breaks spasm ⸻ Step 5: Positive Pressure Ventilation • Tight face mask • 100% oxygen • CPAP 20–40 cm H₂O Avoid excessive pressure in complete obstruction because: • Gastric insufflation • Risk of aspiration ⸻ Step 6: Drugs Succinylcholine (Drug of Choice) IV • 0.1–0.5 mg/kg (break spasm while maintaining spontaneous breathing) • 1–2 mg/kg if rapid paralysis/intubation required IM • 4–5 mg/kg Alternative routes • Intralingual • Intraosseous Always ventilate with 100% oxygen. ⸻ Step 7: Secure Airway If obstruction persists: • Endotracheal intubation • Surgical airway if unable to ventilate/intubate • Cricothyrotomy or tracheostomy ⸻ Adjunctive Therapy Heliox • 70% Helium + 30% Oxygen • Reduces turbulent airflow • Decreases airway resistance • Improves tidal volume ⸻ Racemic Epinephrine • 1 mg of 1:1000 solution • Diluted in 5 mL normal saline • Nebulized every 30 minutes Action • Vasoconstriction • Reduces airway edema ⸻ Steroids Dexamethasone • 0.1 mg/kg IV every 6 hours • Onset: 4–6 hours • Prevents airway edema ⸻ Management of Negative Pressure Pulmonary Edema Occurs due to forceful inspiratory efforts against closed glottis. Management: • 100% oxygen • CPAP/PEEP • Mechanical ventilation if required • Head-end elevation • Diuretics (e.g., furosemide) ⸻ Postoperative Management Remove Irritants • Oropharyngeal suction • Nasopharyngeal suction • Empty NG tube • Remove airway devices Improve Ventilation • Oxygen therapy • CPAP • Larson’s maneuver Drugs • Racemic epinephrine • Heliox • Dexamethasone • Succinylcholine if severe Airway • Reintubation if persistent hypoxia • Do not force ET tube against closed cords • Consider emergency surgical airway ⸻ Prevention of Post-Extubation Laryngospasm • Adequate suctioning before extubation • Extubate in deep anesthesia or fully awake • Lateral recovery position • IV Lidocaine 1–1.5 mg/kg 2–3 minutes before extubation • Topical lidocaine to larynx • IV Magnesium sulphate • Gentle airway manipulation • Avoid extubation during light anesthesia ⸻ Complications • Severe hypoxemia • Hypercarbia • Bradycardia • Cardiac arrest • Aspiration • Negative pressure pulmonary edema • Pulmonary hemorrhage (rare) • Cerebral hypoxia ⸻ Differential Diagnosis of Post-Extubation Stridor • Laryngomalacia • Tracheomalacia • Vocal cord palsy • Laryngeal edema • Airway hematoma • Soft tissue obstruction • Retained throat pack/foreign body • Bronchospasm • Tracheal obstruction ⸻ Important Viva Points • Most common cause: Light plane of anesthesia with airway stimulation. • Most common surgeries: ENT surgeries, especially in children. • Drug of choice: Succinylcholine. • IV dose: 0.1–0.5 mg/kg (up to 1–2 mg/kg if intubation required). • IM dose: 4–5 mg/kg. • Propofol dose: 1–2 mg/kg IV. • Larson’s maneuver: Pressure behind the angle of the mandible anterior to the mastoid process. • CPAP: 20–40 cm H₂O with 100% oxygen. • Major complication: Negative pressure pulmonary edema. • Prevention: Adequate suctioning, awake/deep extubation, IV lidocaine, topical lidocaine, magnesium sulphate, gentle airway manipulation. INTRAOPERATIVE BRONCHOSPASM Definition • Bronchospasm is the sudden contraction of bronchial smooth muscle causing narrowing of the lower airways, increased airway resistance, and airflow obstruction during anesthesia. ⸻ Etiology A. Respiratory Causes Upper Airway • Laryngeal edema • Laryngeal/pharyngeal tumors • Tracheomalacia • Foreign body Lower Airway • Bronchial asthma • COPD • Acute bronchitis • Bronchiectasis • Cystic fibrosis Pulmonary Causes • Pneumonia • Pulmonary edema • Respiratory infection ⸻ B. Mechanical Causes • Airway manipulation • Endotracheal intubation • Endobronchial intubation • Secretions • Mucus plug • Aspiration of gastric contents ⸻ C. Drug-induced Causes Anesthetic agents • Desflurane (especially smokers) • Isoflurane during inhalational induction Other drugs • Morphine • Atracurium • Mivacurium • Rapacuronium • Doxacurium • β-blockers • Aspirin • NSAIDs • Neostigmine • Atropine (>1 mg) • Glycopyrrolate (>0.5 mg) ⸻ D. Allergy/Anaphylaxis • Latex allergy • Antibiotics • IV contrast • Blood transfusion • Muscle relaxants ⸻ E. Other Causes • Light plane of anesthesia • Distended urinary bladder • Peritoneal traction • Smoke inhalation • Carcinoid syndrome ⸻ Causes According to Stage of Anesthesia During Induction • Airway irritation • Intubation • Aspiration • Anaphylaxis • Pulmonary edema • Displaced ETT ⸻ During Maintenance (Most Common) • Anaphylaxis • Aspiration • Airway irritation • Endobronchial intubation • Pneumothorax • Pulmonary edema • Vancomycin • Protamine ⸻ During Emergence • Airway irritation • Extubation • Aspiration • Pulmonary edema • Anaphylaxis • Accidental extubation ⸻ Pathophysiology Trigger ↓ Bronchial smooth muscle contraction ↓ Bronchoconstriction ↓ Increased airway resistance ↓ Air trapping and hyperinflation ↓ V/Q mismatch ↓ Hypoxemia + Hypercapnia ↓ Increased work of breathing ⸻ Clinical Features Clinical Signs • Wheeze (expiratory) • Rhonchi • Prolonged expiration • Cough • Reduced chest expansion • Reduced breath sounds • Silent chest (severe obstruction) ⸻ Ventilator Findings • Increased peak airway pressure • Decreased tidal volume • Difficult manual ventilation • Poor reservoir bag compliance ⸻ Monitoring • Falling SpO₂ • Rising ETCO₂ initially • Severe obstruction → falling ETCO₂ • Hypercarbia • Tachycardia ⸻ Diagnosis Clinical diagnosis based on: • Expiratory wheeze • Increased airway pressure • Difficult ventilation • Decreased tidal volume • Hypoxemia ⸻ Differential Diagnosis Unilateral Wheeze • Endobronchial intubation • Foreign body • Tension pneumothorax • Kinked ETT • Obstructed ETT • Localized pulmonary edema ⸻ Raised Peak Airway Pressure • Bronchospasm • Kinked tube • Secretions • Excessive tidal volume • High inspiratory flow • Pneumothorax • Pleural effusion • Trendelenburg position • Ascites • Pneumoperitoneum • Abdominal packing ⸻ Prevention • Optimize asthma/COPD before surgery • Continue bronchodilators • Steroids in high-risk patients • Prefer LMA over ETT when appropriate • Adequate depth of anesthesia • Gentle airway manipulation • Careful suctioning • Adequate analgesia • Deep extubation in selected patients • Incentive spirometry and early mobilization postoperatively ⸻ Management 1. Call for Help • Inform surgical team • Stop surgery if possible ⸻ 2. Deliver 100% Oxygen • Switch to 100% FiO₂ • Manual ventilation ⸻ 3. Check Mechanical Causes (DOPE) • D – Displacement of ETT • O – Obstruction (secretions, kink, mucus plug) • P – Pneumothorax • E – Equipment failure Suction ETT if required. ⸻ 4. Deepen Anesthesia Preferred agents • Sevoflurane • Isoflurane • Propofol • Ketamine (drug of choice in asthmatics) ⸻ 5. β₂-Agonists (First-line Therapy) Salbutamol • 2.5 mg nebulization • Repeat every 20 minutes × 3 • Then every 2–4 hours Alternative: • MDI through ETT adapter (4–8 puffs) ⸻ Terbutaline • 0.25 mg SC • Every 20 minutes (maximum 3 doses) Others: • Albuterol • Levalbuterol • Pirbuterol ⸻ 6. Anticholinergic Ipratropium bromide • 0.5 mg nebulization • Every 30 minutes × 3 • Then every 2–4 hours ⸻ 7. Corticosteroids Methylprednisolone • 125 mg IV bolus • Then 40–60 mg IV every 6 hours Hydrocortisone • 200 mg IV every 4–6 hours Dexamethasone • IV as adjunct Steroids have delayed onset (4–6 hours). ⸻ 8. Epinephrine Indications: • Severe bronchospasm • Anaphylaxis Dose: • 0.3 mL of 1:1000 IM • IV infusion: 0.5–2 μg/min, titrated as required ⸻ 9. Magnesium Sulphate • 1–2 g IV over 20 minutes • Useful in refractory bronchospasm ⸻ 10. Aminophylline • Loading: 5 mg/kg IV slowly • Infusion: 0.5–0.9 mg/kg/hr (adjust to serum levels) ⸻ 11. Heliox • Helium 70–79% • Oxygen 21–30% Benefits: • Low density gas • Reduces turbulent airflow • Decreases airway resistance • Improves ventilation ⸻ 12. Racemic Epinephrine • Nebulized • Useful when upper airway edema coexists ⸻ If Ventilation Fails • Deepen anesthesia further • Administer muscle relaxant if needed • Exclude pneumothorax • ABG analysis • Mechanical ventilation with prolonged expiratory time • ICU admission if severe ⸻ Ventilator Strategy • Low respiratory rate • Low tidal volume (6–8 mL/kg) • High inspiratory flow • Prolonged expiratory time (I:E 1:3–1:5) • Avoid auto-PEEP • Accept permissive hypercapnia if necessary ⸻ Complications • Hypoxemia • Hypercapnia • Dynamic hyperinflation • Auto-PEEP • Barotrauma • Pneumothorax • Hypotension • Cardiac arrest • Respiratory failure ⸻ Viva Pearls • Most common cause intraoperatively: Airway irritation in a light plane of anesthesia. • Most common cause during maintenance: Anaphylaxis. • Drug of choice in asthmatics: Ketamine. • First-line bronchodilator: Salbutamol. • Steroids do not provide immediate relief (onset 4–6 hours). • Magnesium sulphate: 1–2 g IV in refractory bronchospasm. • Always rule out mechanical causes (DOPE) before diagnosing bronchospasm. • Silent chest indicates severe, life-threatening bronchospasm requiring immediate intervention. Make proper detail notes pdf
mkdir -p /home/daytona/workspace/anesthesia-notes && ls /home/daytona/workspace/…
~/anesthesia-notes/generate_notes.py
from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.units import mm, cm
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.enums import TA_LEFT, TA_CENTER, TA_JUSTIFY
from reportlab.platypus import (
SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
HRFlowable, PageBreak, KeepTogether
)
from reportlab.platypus.flowables import Flowable
from reportlab.lib.colors import HexColor
# ── Color Palette ──────────────────────────────────────────────
DARK_NAVY = HexColor("#1A237E") # main headings
MEDIUM_BLUE = HexColor("#283593") # section headings
ACCENT_BLUE = HexColor("#1565C0") # sub-headings
LIGHT_BLUE = HexColor("#E3F2FD") # header/table bg
MID_BLUE_BG = HexColor("#BBDEFB") # alternate row
TEAL = HexColor("#00695C") # callout / viva
TEAL_BG = HexColor("#E0F2F1") # callout bg
RED_ALERT = HexColor("#C62828") # warnings
RED_BG = HexColor("#FFEBEE") # alert bg
ORANGE = HexColor("#E65100") # step headers
ORANGE_BG = HexColor("#FFF3E0") # step bg
PURPLE = HexColor("#4A148C") # path steps
PURPLE_BG = HexColor("#F3E5F5") # path bg
DARK_TEXT = HexColor("#212121")
MID_TEXT = HexColor("#424242")
LIGHT_GRAY = HexColor("#F5F5F5")
BORDER_GRAY = HexColor("#BDBDBD")
GREEN_DRUG = HexColor("#1B5E20")
GREEN_BG = HexColor("#E8F5E9")
PAGE_W, PAGE_H = A4
# ── Document ────────────────────────────────────────────────────
doc = SimpleDocTemplate(
"/home/daytona/workspace/anesthesia-notes/Airway_Emergencies_Notes.pdf",
pagesize=A4,
leftMargin=18*mm, rightMargin=18*mm,
topMargin=20*mm, bottomMargin=20*mm,
title="Airway Emergencies – Anesthesia Notes",
author="Anesthesia Study Notes"
)
styles = getSampleStyleSheet()
# ── Custom Styles ───────────────────────────────────────────────
def S(name, **kw):
return ParagraphStyle(name, **kw)
COVER_TITLE = S("CoverTitle",
fontName="Helvetica-Bold", fontSize=30, textColor=colors.white,
alignment=TA_CENTER, spaceAfter=8, leading=36)
COVER_SUB = S("CoverSub",
fontName="Helvetica", fontSize=14, textColor=HexColor("#CFD8DC"),
alignment=TA_CENTER, spaceAfter=6, leading=20)
CHAPTER_TITLE = S("ChapterTitle",
fontName="Helvetica-Bold", fontSize=20, textColor=colors.white,
alignment=TA_LEFT, spaceAfter=4, leading=26)
SECTION_HEAD = S("SectionHead",
fontName="Helvetica-Bold", fontSize=13, textColor=DARK_NAVY,
spaceBefore=10, spaceAfter=4, leading=17,
borderPad=4)
SUBSECTION_HEAD = S("SubsectionHead",
fontName="Helvetica-Bold", fontSize=11, textColor=ACCENT_BLUE,
spaceBefore=7, spaceAfter=3, leading=15)
BODY = S("Body",
fontName="Helvetica", fontSize=10, textColor=DARK_TEXT,
leading=14, spaceAfter=3, alignment=TA_JUSTIFY)
BULLET = S("Bullet",
fontName="Helvetica", fontSize=10, textColor=DARK_TEXT,
leading=14, spaceAfter=2, leftIndent=14, firstLineIndent=-10,
alignment=TA_LEFT)
BULLET2 = S("Bullet2",
fontName="Helvetica", fontSize=9.5, textColor=MID_TEXT,
leading=13, spaceAfter=2, leftIndent=28, firstLineIndent=-10,
alignment=TA_LEFT)
BOLD_ITEM = S("BoldItem",
fontName="Helvetica-Bold", fontSize=10, textColor=DARK_NAVY,
leading=14, spaceAfter=2, leftIndent=14, firstLineIndent=-10)
DRUG_STYLE = S("Drug",
fontName="Helvetica-Bold", fontSize=10, textColor=GREEN_DRUG,
leading=14, spaceAfter=2, leftIndent=14, firstLineIndent=-10)
VIVA_BODY = S("VivaBody",
fontName="Helvetica", fontSize=10, textColor=DARK_TEXT,
leading=14, spaceAfter=2, leftIndent=14, firstLineIndent=-10)
ALERT_TEXT = S("Alert",
fontName="Helvetica-Bold", fontSize=10, textColor=RED_ALERT,
leading=14, spaceAfter=2, leftIndent=14, firstLineIndent=-10)
PATH_TEXT = S("PathText",
fontName="Helvetica", fontSize=10, textColor=DARK_TEXT,
leading=16, spaceAfter=0, alignment=TA_CENTER)
PATH_ARROW = S("PathArrow",
fontName="Helvetica-Bold", fontSize=14, textColor=PURPLE,
leading=14, spaceAfter=0, alignment=TA_CENTER)
STEP_TITLE = S("StepTitle",
fontName="Helvetica-Bold", fontSize=11, textColor=ORANGE,
leading=15, spaceAfter=3)
NOTE_STYLE = S("Note",
fontName="Helvetica-Oblique", fontSize=9.5, textColor=HexColor("#5D4037"),
leading=13, spaceAfter=2, leftIndent=14)
# ── Helper Flowables ─────────────────────────────────────────────
def hr(color=BORDER_GRAY, thickness=0.7):
return HRFlowable(width="100%", thickness=thickness, color=color, spaceAfter=4, spaceBefore=4)
def sp(h=4):
return Spacer(1, h*mm)
def section_header(text, bg=DARK_NAVY):
"""Colored bar with white section title."""
data = [[Paragraph(f'<font color="white"><b>{text}</b></font>',
ParagraphStyle("SH", fontName="Helvetica-Bold", fontSize=13,
textColor=colors.white, leading=18))]]
t = Table(data, colWidths=[PAGE_W - 36*mm])
t.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), bg),
("TOPPADDING", (0,0), (-1,-1), 7),
("BOTTOMPADDING", (0,0), (-1,-1), 7),
("LEFTPADDING", (0,0), (-1,-1), 10),
("RIGHTPADDING", (0,0), (-1,-1), 10),
("ROWBACKGROUNDS", (0,0), (-1,-1), [bg]),
]))
return t
def subsection_box(text, bg=LIGHT_BLUE, fg=DARK_NAVY):
data = [[Paragraph(f'<b>{text}</b>',
ParagraphStyle("SSH", fontName="Helvetica-Bold", fontSize=11,
textColor=fg, leading=15))]]
t = Table(data, colWidths=[PAGE_W - 36*mm])
t.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), bg),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING", (0,0), (-1,-1), 5),
("LEFTPADDING", (0,0), (-1,-1), 8),
("RIGHTPADDING", (0,0), (-1,-1), 8),
("LINEBELOW", (0,0), (-1,-1), 1, ACCENT_BLUE),
]))
return t
def step_box(num, title, bg=ORANGE_BG):
data = [[Paragraph(f'<b>STEP {num} — {title}</b>',
ParagraphStyle("ST", fontName="Helvetica-Bold", fontSize=11,
textColor=ORANGE, leading=15))]]
t = Table(data, colWidths=[PAGE_W - 36*mm])
t.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), bg),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING", (0,0), (-1,-1), 5),
("LEFTPADDING", (0,0), (-1,-1), 10),
("RIGHTPADDING", (0,0), (-1,-1), 10),
("LINEBELOW", (0,0), (-1,-1), 1.5, ORANGE),
]))
return t
def alert_box(text):
data = [[Paragraph(f'<b>⚠ {text}</b>',
ParagraphStyle("AB", fontName="Helvetica-Bold", fontSize=10,
textColor=RED_ALERT, leading=14))]]
t = Table(data, colWidths=[PAGE_W - 36*mm])
t.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), RED_BG),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING", (0,0), (-1,-1), 5),
("LEFTPADDING", (0,0), (-1,-1), 10),
("RIGHTPADDING", (0,0), (-1,-1), 10),
("BOX", (0,0), (-1,-1), 1, RED_ALERT),
]))
return t
def viva_box(items):
rows = []
for itm in items:
rows.append([Paragraph(f'<b>{itm[0]}</b>',
ParagraphStyle("VK", fontName="Helvetica-Bold", fontSize=9.5,
textColor=TEAL, leading=13)),
Paragraph(itm[1],
ParagraphStyle("VV", fontName="Helvetica", fontSize=9.5,
textColor=DARK_TEXT, leading=13))])
col1 = 62*mm
col2 = PAGE_W - 36*mm - col1
t = Table(rows, colWidths=[col1, col2])
t.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), TEAL_BG),
("ROWBACKGROUNDS",(0,0), (-1,-1), [TEAL_BG, HexColor("#C8E6C9")]),
("TOPPADDING", (0,0), (-1,-1), 4),
("BOTTOMPADDING", (0,0), (-1,-1), 4),
("LEFTPADDING", (0,0), (-1,-1), 6),
("RIGHTPADDING", (0,0), (-1,-1), 6),
("LINEBELOW", (0,0), (-1,-1), 0.5, HexColor("#A5D6A7")),
("BOX", (0,0), (-1,-1), 1, TEAL),
("VALIGN", (0,0), (-1,-1), "TOP"),
]))
return t
def two_col_table(rows, col_ratios=(0.45, 0.55), header=None, alt=True):
w = PAGE_W - 36*mm
c1 = w * col_ratios[0]
c2 = w * col_ratios[1]
tbl_data = []
if header:
tbl_data.append([
Paragraph(f'<b>{header[0]}</b>', ParagraphStyle("TH", fontName="Helvetica-Bold",
fontSize=10, textColor=colors.white, leading=13)),
Paragraph(f'<b>{header[1]}</b>', ParagraphStyle("TH", fontName="Helvetica-Bold",
fontSize=10, textColor=colors.white, leading=13))
])
for r in rows:
tbl_data.append([
Paragraph(r[0], ParagraphStyle("TC", fontName="Helvetica-Bold", fontSize=9.5,
textColor=DARK_NAVY, leading=13)),
Paragraph(r[1], ParagraphStyle("TV", fontName="Helvetica", fontSize=9.5,
textColor=DARK_TEXT, leading=13))
])
style = [
("TOPPADDING", (0,0), (-1,-1), 4),
("BOTTOMPADDING", (0,0), (-1,-1), 4),
("LEFTPADDING", (0,0), (-1,-1), 6),
("RIGHTPADDING", (0,0), (-1,-1), 6),
("LINEBELOW", (0,0), (-1,-1), 0.5, BORDER_GRAY),
("BOX", (0,0), (-1,-1), 1, BORDER_GRAY),
("VALIGN", (0,0), (-1,-1), "TOP"),
]
if header:
style += [("BACKGROUND", (0,0), (-1,0), DARK_NAVY)]
if alt:
for i in range(1 if header else 0, len(tbl_data), 2):
style.append(("BACKGROUND", (0,i), (-1,i), LIGHT_GRAY))
t = Table(tbl_data, colWidths=[c1, c2])
t.setStyle(TableStyle(style))
return t
def pathway_table(steps):
"""Renders a vertical pathway with arrows."""
rows = []
for i, step in enumerate(steps):
rows.append([Paragraph(step, ParagraphStyle("PS",
fontName="Helvetica", fontSize=10, textColor=DARK_TEXT,
leading=14, alignment=TA_CENTER))])
if i < len(steps)-1:
rows.append([Paragraph('<font color="#4A148C"><b>↓</b></font>',
ParagraphStyle("PA", fontName="Helvetica-Bold", fontSize=16,
textColor=PURPLE, leading=16, alignment=TA_CENTER))])
w = PAGE_W - 36*mm
t = Table(rows, colWidths=[w])
style_cmds = [
("TOPPADDING", (0,0), (-1,-1), 3),
("BOTTOMPADDING", (0,0), (-1,-1), 3),
("LEFTPADDING", (0,0), (-1,-1), 6),
("RIGHTPADDING", (0,0), (-1,-1), 6),
("ALIGN", (0,0), (-1,-1), "CENTER"),
("VALIGN", (0,0), (-1,-1), "MIDDLE"),
]
for i in range(0, len(rows), 2):
style_cmds.append(("BACKGROUND", (0,i), (-1,i), PURPLE_BG))
style_cmds.append(("BOX", (0,i), (-1,i), 0.5, PURPLE))
style_cmds.append(("ROUNDEDCORNERS", [4]))
t.setStyle(TableStyle(style_cmds))
return t
def drug_table(rows, header=None):
w = PAGE_W - 36*mm
tbl_data = []
if header:
tbl_data.append([
Paragraph(f'<b>{h}</b>', ParagraphStyle("DH", fontName="Helvetica-Bold",
fontSize=10, textColor=colors.white, leading=13)) for h in header
])
for r in rows:
tbl_data.append([
Paragraph(str(c), ParagraphStyle("DC", fontName="Helvetica", fontSize=9.5,
textColor=DARK_TEXT, leading=13)) for c in r
])
n = len(rows[0])
col_w = w / n
style = [
("TOPPADDING", (0,0), (-1,-1), 4),
("BOTTOMPADDING", (0,0), (-1,-1), 4),
("LEFTPADDING", (0,0), (-1,-1), 6),
("RIGHTPADDING", (0,0), (-1,-1), 6),
("LINEBELOW", (0,0), (-1,-1), 0.5, BORDER_GRAY),
("BOX", (0,0), (-1,-1), 1, BORDER_GRAY),
("VALIGN", (0,0), (-1,-1), "TOP"),
]
if header:
style += [("BACKGROUND", (0,0), (-1,0), GREEN_DRUG)]
for i in range(1 if header else 0, len(tbl_data), 2):
style.append(("BACKGROUND", (0,i), (-1,i), GREEN_BG))
t = Table(tbl_data, colWidths=[col_w]*n)
t.setStyle(TableStyle(style))
return t
def b(text): return f"<b>{text}</b>"
def bullet(text, style=None): return Paragraph(f"\u2022 {text}", style or BULLET)
def bullet2(text): return Paragraph(f" \u25e6 {text}", BULLET2)
def bold_bullet(text): return Paragraph(f"\u2022 {text}", BOLD_ITEM)
# ═══════════════════════════════════════════════════════════════
# BUILD STORY
# ═══════════════════════════════════════════════════════════════
story = []
# ── COVER PAGE ──────────────────────────────────────────────────
cover_data = [[
Paragraph("ANESTHESIA NOTES", COVER_SUB),
Paragraph("AIRWAY EMERGENCIES", COVER_TITLE),
Paragraph("Laryngospasm & Intraoperative Bronchospasm", COVER_SUB),
Spacer(1, 6*mm),
Paragraph("Comprehensive Study Notes for Medical Examinations",
ParagraphStyle("CS2", fontName="Helvetica-Oblique", fontSize=11,
textColor=HexColor("#90CAF9"), alignment=TA_CENTER, leading=16)),
]]
cover_t = Table(cover_data, colWidths=[PAGE_W - 36*mm])
cover_t.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), DARK_NAVY),
("TOPPADDING", (0,0), (-1,-1), 28*mm),
("BOTTOMPADDING", (0,0), (-1,-1), 28*mm),
("LEFTPADDING", (0,0), (-1,-1), 12*mm),
("RIGHTPADDING", (0,0), (-1,-1), 12*mm),
("ALIGN", (0,0), (-1,-1), "CENTER"),
("VALIGN", (0,0), (-1,-1), "MIDDLE"),
]))
story.append(cover_t)
story.append(sp(8))
# Quick overview table
overview = [
["Topic", "Key Drug", "Key Maneuver", "Main Complication"],
["Laryngospasm", "Succinylcholine (DOC)", "Larson's Maneuver", "Negative Pressure Pulmonary Edema"],
["Bronchospasm", "Salbutamol / Ketamine", "DOPE Check", "Auto-PEEP / Barotrauma"],
]
ov_data = []
for i, row in enumerate(overview):
if i == 0:
ov_data.append([Paragraph(f"<b>{c}</b>",
ParagraphStyle("OH", fontName="Helvetica-Bold", fontSize=9.5,
textColor=colors.white, leading=13)) for c in row])
else:
ov_data.append([Paragraph(c,
ParagraphStyle("OB", fontName="Helvetica", fontSize=9.5,
textColor=DARK_TEXT, leading=13)) for c in row])
w = PAGE_W - 36*mm
ov_t = Table(ov_data, colWidths=[w*0.25, w*0.28, w*0.25, w*0.22])
ov_t.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,0), MEDIUM_BLUE),
("BACKGROUND", (0,1), (-1,1), LIGHT_BLUE),
("BACKGROUND", (0,2), (-1,2), MID_BLUE_BG),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING", (0,0), (-1,-1), 5),
("LEFTPADDING", (0,0), (-1,-1), 6),
("RIGHTPADDING", (0,0), (-1,-1), 6),
("BOX", (0,0), (-1,-1), 1, DARK_NAVY),
("LINEBELOW", (0,0), (-1,-1), 0.5, BORDER_GRAY),
("VALIGN", (0,0), (-1,-1), "MIDDLE"),
]))
story.append(ov_t)
story.append(PageBreak())
# ═══════════════════════════════════════════════════════════════
# PART 1 – LARYNGOSPASM
# ═══════════════════════════════════════════════════════════════
# Chapter header
ch1_data = [[Paragraph("PART I", ParagraphStyle("CH1S", fontName="Helvetica",
fontSize=12, textColor=HexColor("#90CAF9"), leading=15)),
Paragraph("LARYNGOSPASM", CHAPTER_TITLE)]]
ch1_t = Table(ch1_data, colWidths=[20*mm, PAGE_W - 56*mm])
ch1_t.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), DARK_NAVY),
("TOPPADDING", (0,0), (-1,-1), 10),
("BOTTOMPADDING", (0,0), (-1,-1), 10),
("LEFTPADDING", (0,0), (-1,-1), 10),
("RIGHTPADDING", (0,0), (-1,-1), 10),
("VALIGN", (0,0), (-1,-1), "MIDDLE"),
]))
story.append(ch1_t)
story.append(sp(4))
# Definition
story.append(section_header("DEFINITION"))
story.append(sp(2))
story.append(Paragraph(
"Laryngospasm is an <b>exaggerated protective glottic closure reflex</b> causing "
"<b>forceful closure of the vocal cords</b> that persists even after the initiating "
"stimulus is removed, leading to <b>partial or complete upper airway obstruction</b>.",
ParagraphStyle("DefBox", fontName="Helvetica", fontSize=10.5, textColor=DARK_TEXT,
leading=16, spaceAfter=4, borderPad=8,
backColor=LIGHT_BLUE, borderWidth=1, borderColor=ACCENT_BLUE,
borderRadius=4, leftIndent=4, rightIndent=4, alignment=TA_JUSTIFY)
))
story.append(sp(3))
# Etiology
story.append(section_header("ETIOLOGY"))
story.append(sp(2))
etio_headers = ["Category", "Specific Factors"]
etio_rows = [
["<b>Patient Factors</b>", "Pediatric age group (most common), URTI, Chronic smokers, Reactive airway disease"],
["<b>Surgery Factors</b>", "ENT surgeries (tonsil/adenoid), Airway procedures, Endoscopy, Esophagoscopy, Upper abdominal / Pelvic / Thoracic surgeries"],
["<b>Light Anesthesia</b>", "<b>Most common intraoperative cause</b> of laryngospasm"],
["<b>Drug-related</b>", "Morphine, Barbiturates (increase parasympathetic activity)"],
["<b>Volatile Agents</b>", "Irritancy: Ether > Isoflurane > Halothane > Enflurane (Sevoflurane = least irritant)"],
["<b>Airway Irritants</b>", "Secretions, blood, vomitus, suctioning, NG tube, oral/nasal airways, nasal irritation"],
]
story.append(two_col_table(etio_rows, header=etio_headers))
story.append(sp(3))
# Postoperative Causes
story.append(subsection_box("Postoperative Causes"))
story.append(sp(2))
po_rows = [
["<b>Immediate</b>", "Extubation during light anesthesia (especially post-halothane)"],
["<b>Delayed</b>", "Secretions around cords, regurgitated gastric contents, silent aspiration beside NG tube, airway irritation, hypoxia-induced vomiting, epiglottitis"],
]
story.append(two_col_table(po_rows))
story.append(sp(4))
# Pathophysiology
story.append(section_header("PATHOPHYSIOLOGY"))
story.append(sp(3))
story.append(pathway_table([
"Stimulus (Secretions / Blood / Light Anesthesia / Airway Manipulation)",
"Superior Laryngeal Nerve Activation",
"Vagus Nerve (Reflex Arc)",
"Reflex Contraction of Laryngeal Adductor Muscles",
"Complete or Partial Glottic Closure → Airway Obstruction",
"Hypoxia + Hypercarbia",
"Forceful Inspiratory Efforts Against Closed Glottis",
"Negative Intrathoracic Pressure",
"Negative Pressure Pulmonary Edema (NPPE)",
]))
story.append(sp(4))
# Clinical Features
story.append(section_header("CLINICAL FEATURES"))
story.append(sp(2))
cf_data = [
[Paragraph("<b>Stage</b>", ParagraphStyle("CH", fontName="Helvetica-Bold", fontSize=10,
textColor=colors.white, leading=13)),
Paragraph("<b>Signs & Symptoms</b>", ParagraphStyle("CH", fontName="Helvetica-Bold",
fontSize=10, textColor=colors.white, leading=13))],
[Paragraph("<b>Early</b>", ParagraphStyle("CK", fontName="Helvetica-Bold", fontSize=10,
textColor=DARK_NAVY, leading=13)),
Paragraph("Inspiratory stridor, Dyspnea, Tachypnea, Anxiety, Use of accessory muscles",
ParagraphStyle("CV", fontName="Helvetica", fontSize=9.5, textColor=DARK_TEXT, leading=13))],
[Paragraph("<b>Progressive</b>", ParagraphStyle("CK", fontName="Helvetica-Bold", fontSize=10,
textColor=DARK_NAVY, leading=13)),
Paragraph("Intercostal retractions, Suprasternal indrawing, Tracheal tug, "
"Paradoxical chest & abdominal movements (chest sinks while abdomen rises)",
ParagraphStyle("CV", fontName="Helvetica", fontSize=9.5, textColor=DARK_TEXT, leading=13))],
[Paragraph("<b>Complete Obstruction</b>", ParagraphStyle("CK", fontName="Helvetica-Bold", fontSize=10,
textColor=RED_ALERT, leading=13)),
Paragraph("<b>Silent chest, No air entry, Rapid desaturation, Cyanosis, "
"Bradycardia (especially children), Cardiac arrest if untreated</b>",
ParagraphStyle("CV", fontName="Helvetica-Bold", fontSize=9.5, textColor=RED_ALERT, leading=13))],
]
w = PAGE_W - 36*mm
cf_t = Table(cf_data, colWidths=[w*0.25, w*0.75])
cf_t.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,0), DARK_NAVY),
("BACKGROUND", (0,1), (-1,1), LIGHT_GRAY),
("BACKGROUND", (0,2), (-1,2), LIGHT_BLUE),
("BACKGROUND", (0,3), (-1,3), RED_BG),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING", (0,0), (-1,-1), 5),
("LEFTPADDING", (0,0), (-1,-1), 6),
("RIGHTPADDING", (0,0), (-1,-1), 6),
("BOX", (0,0), (-1,-1), 1, DARK_NAVY),
("LINEBELOW", (0,0), (-1,-1), 0.5, BORDER_GRAY),
("VALIGN", (0,0), (-1,-1), "MIDDLE"),
]))
story.append(cf_t)
story.append(sp(4))
# Management
story.append(section_header("MANAGEMENT — STEP-BY-STEP"))
story.append(sp(2))
# Step 1
story.append(KeepTogether([
step_box(1, "Call for Help"),
sp(1),
bullet("Stop surgery and inform surgeon"),
bullet("Administer <b>100% oxygen</b> immediately"),
sp(2),
]))
# Step 2
story.append(KeepTogether([
step_box(2, "Remove the Stimulus"),
sp(1),
bullet("Stop surgical stimulation"),
bullet("Suction blood and secretions from airway"),
bullet("Remove airway irritants (NG tube, airways if indicated)"),
sp(2),
]))
# Step 3
story.append(KeepTogether([
step_box(3, "Deepen Anesthesia"),
sp(1),
bullet("<b>Propofol 1–2 mg/kg IV</b> — Drug of choice for deepening"),
bullet("Volatile: <b>Sevoflurane</b> preferred (least irritant); avoid ether/desflurane"),
sp(2),
]))
# Step 4
story.append(KeepTogether([
step_box(4, "Open the Airway"),
sp(1),
bullet("Head extension, Jaw thrust, Chin lift"),
bullet("<b>Larson's Maneuver:</b> Bilateral firm pressure in the <b>laryngospasm notch</b> — "
"located behind the angle of the mandible, just anterior to the mastoid process. "
"Stimulates the painful pressure point and reflexively breaks the spasm."),
sp(2),
]))
# Step 5
story.append(KeepTogether([
step_box(5, "Positive Pressure Ventilation"),
sp(1),
bullet("<b>Tight-fitting face mask</b> with 100% oxygen"),
bullet("<b>CPAP 20–40 cm H<sub rise='3' size='7'>2</sub>O</b>"),
alert_box("Do NOT use excessive pressure in complete obstruction — risk of gastric insufflation and aspiration"),
sp(2),
]))
# Step 6 — Drugs
story.append(KeepTogether([
step_box(6, "Pharmacological Management"),
sp(1),
Paragraph("Succinylcholine — Drug of Choice", ParagraphStyle("DHead",
fontName="Helvetica-Bold", fontSize=10.5, textColor=GREEN_DRUG, leading=15, spaceBefore=4)),
sp(1),
]))
sux_rows = [
["<b>Route</b>", "<b>Dose</b>", "<b>Indication</b>"],
["IV (low)", "0.1–0.5 mg/kg", "Break spasm while maintaining spontaneous breathing"],
["IV (full)", "1–2 mg/kg", "Rapid paralysis / endotracheal intubation required"],
["IM", "4–5 mg/kg", "No IV access available"],
["Intralingual / IO", "Same as IM", "Alternative routes when IV unavailable"],
]
s_data = []
for i, row in enumerate(sux_rows):
s_data.append([Paragraph(c, ParagraphStyle("ST2",
fontName="Helvetica-Bold" if i == 0 else "Helvetica",
fontSize=9.5, textColor=colors.white if i==0 else DARK_TEXT, leading=13)) for c in row])
w = PAGE_W - 36*mm
sux_t = Table(s_data, colWidths=[w*0.2, w*0.25, w*0.55])
sux_t.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,0), GREEN_DRUG),
("BACKGROUND", (0,2), (-1,2), GREEN_BG),
("BACKGROUND", (0,4), (-1,4), GREEN_BG),
("TOPPADDING", (0,0), (-1,-1), 4),
("BOTTOMPADDING", (0,0), (-1,-1), 4),
("LEFTPADDING", (0,0), (-1,-1), 6),
("RIGHTPADDING", (0,0), (-1,-1), 6),
("BOX", (0,0), (-1,-1), 1, GREEN_DRUG),
("LINEBELOW", (0,0), (-1,-1), 0.5, BORDER_GRAY),
("VALIGN", (0,0), (-1,-1), "MIDDLE"),
]))
story.append(sux_t)
story.append(sp(2))
story.append(Paragraph("<i>Always ventilate with 100% oxygen after succinylcholine administration.</i>", NOTE_STYLE))
story.append(sp(3))
# Step 7
story.append(KeepTogether([
step_box(7, "Secure the Airway"),
sp(1),
bullet("If obstruction persists: <b>Endotracheal intubation</b>"),
bullet("<b>Surgical airway</b> (cricothyrotomy or tracheostomy) if cannot ventilate or intubate"),
sp(3),
]))
# Adjunctive Therapy
story.append(section_header("ADJUNCTIVE THERAPY"))
story.append(sp(2))
adj_data = [
["<b>Agent</b>", "<b>Dose / Preparation</b>", "<b>Mechanism / Notes</b>"],
["Heliox", "70% Helium + 30% Oxygen", "Low-density gas; reduces turbulent airflow; decreases airway resistance; improves tidal volume"],
["Racemic Epinephrine", "1 mg of 1:1000 diluted in 5 mL NS; nebulized every 30 min", "Vasoconstriction; reduces airway mucosal edema"],
["Dexamethasone", "0.1 mg/kg IV every 6 hours", "Prevents airway edema; onset 4–6 hours"],
]
a_data = []
for i, row in enumerate(adj_data):
a_data.append([Paragraph(c, ParagraphStyle("AT",
fontName="Helvetica-Bold" if i == 0 else "Helvetica",
fontSize=9.5, textColor=colors.white if i==0 else DARK_TEXT, leading=13)) for c in row])
w = PAGE_W - 36*mm
adj_t = Table(a_data, colWidths=[w*0.2, w*0.33, w*0.47])
adj_t.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,0), MEDIUM_BLUE),
("BACKGROUND", (0,2), (-1,2), LIGHT_BLUE),
("TOPPADDING", (0,0), (-1,-1), 4),
("BOTTOMPADDING", (0,0), (-1,-1), 4),
("LEFTPADDING", (0,0), (-1,-1), 6),
("RIGHTPADDING", (0,0), (-1,-1), 6),
("BOX", (0,0), (-1,-1), 1, MEDIUM_BLUE),
("LINEBELOW", (0,0), (-1,-1), 0.5, BORDER_GRAY),
("VALIGN", (0,0), (-1,-1), "TOP"),
]))
story.append(adj_t)
story.append(sp(3))
# NPPE Management
story.append(subsection_box("Management of Negative Pressure Pulmonary Edema (NPPE)", bg=RED_BG, fg=RED_ALERT))
story.append(sp(1))
story.append(Paragraph(
"<i>Occurs due to forceful inspiratory efforts against closed glottis, generating extreme "
"negative intrathoracic pressure → pulmonary capillary fluid transudation.</i>",
ParagraphStyle("NI", fontName="Helvetica-Oblique", fontSize=9.5, textColor=MID_TEXT,
leading=13, spaceAfter=4)))
story.append(sp(1))
nppe_cols = [
bullet("100% oxygen administration"),
bullet("CPAP / PEEP ventilation"),
bullet("Mechanical ventilation if required"),
bullet("Head-end elevation (30°)"),
bullet("Diuretics: Furosemide"),
]
for b_ in nppe_cols:
story.append(b_)
story.append(sp(3))
# Prevention
story.append(section_header("PREVENTION OF POST-EXTUBATION LARYNGOSPASM"))
story.append(sp(2))
prev_items = [
"Adequate suctioning before extubation (oropharyngeal + nasopharyngeal)",
"Extubate in <b>deep anesthesia</b> OR when <b>fully awake</b> — avoid light/intermediate plane",
"Lateral recovery position",
"<b>IV Lidocaine 1–1.5 mg/kg</b> given 2–3 minutes before extubation",
"Topical lidocaine applied to the larynx",
"<b>IV Magnesium sulphate</b> — reduces neuromuscular excitability",
"Gentle airway manipulation throughout the procedure",
]
for item in prev_items:
story.append(bullet(item))
story.append(sp(3))
# Differential Diagnosis
story.append(subsection_box("Differential Diagnosis of Post-Extubation Stridor"))
story.append(sp(2))
dd_ls = [
"Laryngomalacia / Tracheomalacia", "Vocal cord palsy", "Laryngeal edema",
"Airway hematoma", "Soft tissue obstruction", "Retained throat pack / Foreign body",
"Bronchospasm", "Tracheal obstruction"
]
w = PAGE_W - 36*mm
dd_data = [[
Paragraph(f"\u2022 {dd_ls[i]}", BULLET),
Paragraph(f"\u2022 {dd_ls[i+1]}" if i+1 < len(dd_ls) else "", BULLET)
] for i in range(0, len(dd_ls), 2)]
dd_t = Table(dd_data, colWidths=[w*0.5, w*0.5])
dd_t.setStyle(TableStyle([
("TOPPADDING", (0,0), (-1,-1), 3),
("BOTTOMPADDING", (0,0), (-1,-1), 3),
("LEFTPADDING", (0,0), (-1,-1), 4),
("RIGHTPADDING", (0,0), (-1,-1), 4),
("VALIGN", (0,0), (-1,-1), "TOP"),
]))
story.append(dd_t)
story.append(sp(3))
# VIVA – Laryngospasm
story.append(section_header("VIVA POINTS — LARYNGOSPASM", bg=TEAL))
story.append(sp(2))
viva1 = [
("Most common cause:", "Light plane of anesthesia with airway stimulation"),
("Most common surgeries:", "ENT surgeries, especially in children"),
("Drug of choice:", "Succinylcholine"),
("IV dose (spasm):", "0.1–0.5 mg/kg IV"),
("IV dose (intubation):", "1–2 mg/kg IV"),
("IM dose:", "4–5 mg/kg IM"),
("Propofol dose:", "1–2 mg/kg IV (to deepen anesthesia)"),
("Larson's Maneuver:", "Firm pressure posterior to angle of mandible, anterior to mastoid"),
("CPAP pressure:", "20–40 cm H2O with 100% O2"),
("Major complication:", "Negative Pressure Pulmonary Edema (NPPE)"),
("Least irritant volatile:", "Sevoflurane"),
("Prevention:", "Adequate suctioning, awake/deep extubation, IV/topical lidocaine, MgSO4"),
]
story.append(viva_box(viva1))
story.append(PageBreak())
# ═══════════════════════════════════════════════════════════════
# PART 2 – INTRAOPERATIVE BRONCHOSPASM
# ═══════════════════════════════════════════════════════════════
ch2_data = [[Paragraph("PART II", ParagraphStyle("CH2S", fontName="Helvetica",
fontSize=12, textColor=HexColor("#90CAF9"), leading=15)),
Paragraph("INTRAOPERATIVE BRONCHOSPASM", CHAPTER_TITLE)]]
ch2_t = Table(ch2_data, colWidths=[20*mm, PAGE_W - 56*mm])
ch2_t.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), DARK_NAVY),
("TOPPADDING", (0,0), (-1,-1), 10),
("BOTTOMPADDING", (0,0), (-1,-1), 10),
("LEFTPADDING", (0,0), (-1,-1), 10),
("RIGHTPADDING", (0,0), (-1,-1), 10),
("VALIGN", (0,0), (-1,-1), "MIDDLE"),
]))
story.append(ch2_t)
story.append(sp(4))
# Definition
story.append(section_header("DEFINITION"))
story.append(sp(2))
story.append(Paragraph(
"Bronchospasm is the <b>sudden contraction of bronchial smooth muscle</b> causing "
"<b>narrowing of the lower airways</b>, increased airway resistance, and airflow "
"obstruction occurring during the perioperative period.",
ParagraphStyle("DefBox2", fontName="Helvetica", fontSize=10.5, textColor=DARK_TEXT,
leading=16, spaceAfter=4, borderPad=8,
backColor=LIGHT_BLUE, borderWidth=1, borderColor=ACCENT_BLUE,
borderRadius=4, leftIndent=4, rightIndent=4, alignment=TA_JUSTIFY)
))
story.append(sp(3))
# Etiology
story.append(section_header("ETIOLOGY"))
story.append(sp(2))
story.append(subsection_box("A. Respiratory Causes"))
story.append(sp(1))
resp_r = [
["<b>Upper Airway</b>", "Laryngeal edema, Pharyngeal/laryngeal tumors, Tracheomalacia, Foreign body"],
["<b>Lower Airway</b>", "Bronchial asthma, COPD, Acute bronchitis, Bronchiectasis, Cystic fibrosis"],
["<b>Pulmonary</b>", "Pneumonia, Pulmonary edema, Respiratory infections"],
]
story.append(two_col_table(resp_r))
story.append(sp(2))
story.append(subsection_box("B. Mechanical Causes"))
story.append(sp(1))
mech_items = ["Airway manipulation / Endotracheal intubation",
"Endobronchial intubation",
"Secretions / Mucus plug",
"Aspiration of gastric contents"]
for m in mech_items:
story.append(bullet(m))
story.append(sp(2))
story.append(subsection_box("C. Drug-Induced Causes"))
story.append(sp(1))
drug_rows = [
["<b>Volatile Agents</b>", "Desflurane (especially smokers), Isoflurane (induction)"],
["<b>Opioids</b>", "Morphine (histamine release)"],
["<b>Muscle Relaxants</b>", "Atracurium, Mivacurium, Rapacuronium, Doxacurium"],
["<b>Cardiovascular</b>", "Beta-blockers"],
["<b>Analgesics</b>", "Aspirin, NSAIDs"],
["<b>Reversal Agents</b>", "Neostigmine"],
["<b>Anticholinergics (high dose)</b>", "Atropine (>1 mg), Glycopyrrolate (>0.5 mg)"],
]
story.append(two_col_table(drug_rows))
story.append(sp(2))
story.append(subsection_box("D. Allergy / Anaphylaxis"))
story.append(sp(1))
allergy = ["Latex allergy", "Antibiotics", "IV contrast media", "Blood transfusion", "Muscle relaxants"]
for a_ in allergy:
story.append(bullet(a_))
story.append(sp(2))
story.append(subsection_box("E. Other Causes"))
story.append(sp(1))
other_c = ["Light plane of anesthesia", "Distended urinary bladder", "Peritoneal traction",
"Smoke inhalation", "Carcinoid syndrome", "Vancomycin (during maintenance)", "Protamine"]
for o_ in other_c:
story.append(bullet(o_))
story.append(sp(3))
# Stage-wise causes
story.append(section_header("CAUSES BY ANESTHETIC STAGE"))
story.append(sp(2))
stage_data = [
[Paragraph("<b>Stage</b>", ParagraphStyle("SH2", fontName="Helvetica-Bold", fontSize=10,
textColor=colors.white, leading=13)),
Paragraph("<b>Common Causes</b>", ParagraphStyle("SH2", fontName="Helvetica-Bold", fontSize=10,
textColor=colors.white, leading=13))],
[Paragraph("<b>Induction</b>", ParagraphStyle("SK", fontName="Helvetica-Bold", fontSize=10,
textColor=DARK_NAVY, leading=13)),
Paragraph("Airway irritation, Intubation, Aspiration, Anaphylaxis, Pulmonary edema, Displaced ETT",
ParagraphStyle("SV", fontName="Helvetica", fontSize=9.5, textColor=DARK_TEXT, leading=13))],
[Paragraph("<b>Maintenance</b> (Most Common)", ParagraphStyle("SK", fontName="Helvetica-Bold", fontSize=10,
textColor=DARK_NAVY, leading=13)),
Paragraph("<b>Anaphylaxis</b>, Aspiration, Airway irritation, Endobronchial intubation, "
"Pneumothorax, Pulmonary edema, Vancomycin, Protamine",
ParagraphStyle("SV", fontName="Helvetica", fontSize=9.5, textColor=DARK_TEXT, leading=13))],
[Paragraph("<b>Emergence</b>", ParagraphStyle("SK", fontName="Helvetica-Bold", fontSize=10,
textColor=DARK_NAVY, leading=13)),
Paragraph("Airway irritation, Extubation, Aspiration, Pulmonary edema, Anaphylaxis, Accidental extubation",
ParagraphStyle("SV", fontName="Helvetica", fontSize=9.5, textColor=DARK_TEXT, leading=13))],
]
w = PAGE_W - 36*mm
stage_t = Table(stage_data, colWidths=[w*0.28, w*0.72])
stage_t.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,0), MEDIUM_BLUE),
("BACKGROUND", (0,1), (-1,1), LIGHT_GRAY),
("BACKGROUND", (0,2), (-1,2), LIGHT_BLUE),
("BACKGROUND", (0,3), (-1,3), LIGHT_GRAY),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING", (0,0), (-1,-1), 5),
("LEFTPADDING", (0,0), (-1,-1), 6),
("RIGHTPADDING", (0,0), (-1,-1), 6),
("BOX", (0,0), (-1,-1), 1, MEDIUM_BLUE),
("LINEBELOW", (0,0), (-1,-1), 0.5, BORDER_GRAY),
("VALIGN", (0,0), (-1,-1), "MIDDLE"),
]))
story.append(stage_t)
story.append(sp(3))
# Pathophysiology
story.append(section_header("PATHOPHYSIOLOGY"))
story.append(sp(3))
story.append(pathway_table([
"Trigger (Intubation / Aspiration / Anaphylaxis / Drug / Light Anesthesia)",
"Bronchial Smooth Muscle Contraction",
"Bronchoconstriction → Increased Airway Resistance",
"Air Trapping and Dynamic Hyperinflation",
"V/Q Mismatch",
"Hypoxemia + Hypercapnia",
"Increased Work of Breathing → Respiratory Failure",
]))
story.append(sp(3))
# Clinical Features
story.append(section_header("CLINICAL FEATURES"))
story.append(sp(2))
story.append(subsection_box("Clinical Signs"))
story.append(sp(1))
cl_signs = [
"Expiratory wheeze and rhonchi", "Prolonged expiratory phase",
"Cough", "Reduced chest expansion", "Reduced breath sounds",
"<b>Silent chest = severe life-threatening obstruction</b>"
]
for s_ in cl_signs:
story.append(bullet(s_))
story.append(sp(2))
story.append(subsection_box("Ventilator / Monitoring Findings"))
story.append(sp(1))
vent_rows = [
["<b>Ventilator</b>", "Increased peak airway pressure, Decreased tidal volume, Difficult manual ventilation, Poor reservoir bag compliance"],
["<b>SpO2</b>", "Falling oxygen saturation"],
["<b>ETCO2</b>", "Initially rising (hypercapnia); severely falling in complete obstruction"],
["<b>Other</b>", "Tachycardia, Hypercarbia on ABG"],
]
story.append(two_col_table(vent_rows))
story.append(sp(3))
# Differential Diagnosis
story.append(section_header("DIFFERENTIAL DIAGNOSIS"))
story.append(sp(2))
dd_data2 = [
[Paragraph("<b>Unilateral Wheeze</b>",
ParagraphStyle("DD", fontName="Helvetica-Bold", fontSize=10, textColor=DARK_NAVY, leading=13)),
Paragraph("Endobronchial intubation, Foreign body, Tension pneumothorax, Kinked/obstructed ETT, Localized pulmonary edema",
ParagraphStyle("DV", fontName="Helvetica", fontSize=9.5, textColor=DARK_TEXT, leading=13))],
[Paragraph("<b>Raised Peak Airway Pressure</b>",
ParagraphStyle("DD", fontName="Helvetica-Bold", fontSize=10, textColor=DARK_NAVY, leading=13)),
Paragraph("Bronchospasm, Kinked/blocked tube, Secretions, Excessive TV, High flow rate, "
"Pneumothorax, Pleural effusion, Trendelenburg, Ascites, Pneumoperitoneum, Abdominal packing",
ParagraphStyle("DV", fontName="Helvetica", fontSize=9.5, textColor=DARK_TEXT, leading=13))],
]
w = PAGE_W - 36*mm
dd2_t = Table(dd_data2, colWidths=[w*0.30, w*0.70])
dd2_t.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,0), LIGHT_BLUE),
("BACKGROUND", (0,1), (-1,1), MID_BLUE_BG),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING", (0,0), (-1,-1), 5),
("LEFTPADDING", (0,0), (-1,-1), 6),
("RIGHTPADDING", (0,0), (-1,-1), 6),
("BOX", (0,0), (-1,-1), 1, MEDIUM_BLUE),
("LINEBELOW", (0,0), (-1,-1), 0.5, BORDER_GRAY),
("VALIGN", (0,0), (-1,-1), "TOP"),
]))
story.append(dd2_t)
story.append(sp(3))
# Management
story.append(section_header("MANAGEMENT — STEP-BY-STEP"))
story.append(sp(2))
# DOPE Box
dope_data = [
[Paragraph("<b>D</b>", ParagraphStyle("DL", fontName="Helvetica-Bold", fontSize=16,
textColor=colors.white, leading=20, alignment=TA_CENTER)),
Paragraph("<b>isplacement of ETT</b>",
ParagraphStyle("DV2", fontName="Helvetica", fontSize=10, textColor=DARK_TEXT, leading=14))],
[Paragraph("<b>O</b>", ParagraphStyle("DL", fontName="Helvetica-Bold", fontSize=16,
textColor=colors.white, leading=20, alignment=TA_CENTER)),
Paragraph("<b>bstruction</b> — secretions, kink, mucus plug",
ParagraphStyle("DV2", fontName="Helvetica", fontSize=10, textColor=DARK_TEXT, leading=14))],
[Paragraph("<b>P</b>", ParagraphStyle("DL", fontName="Helvetica-Bold", fontSize=16,
textColor=colors.white, leading=20, alignment=TA_CENTER)),
Paragraph("<b>neumothorax</b>",
ParagraphStyle("DV2", fontName="Helvetica", fontSize=10, textColor=DARK_TEXT, leading=14))],
[Paragraph("<b>E</b>", ParagraphStyle("DL", fontName="Helvetica-Bold", fontSize=16,
textColor=colors.white, leading=20, alignment=TA_CENTER)),
Paragraph("<b>quipment failure</b>",
ParagraphStyle("DV2", fontName="Helvetica", fontSize=10, textColor=DARK_TEXT, leading=14))],
]
w = PAGE_W - 36*mm
dope_t = Table(dope_data, colWidths=[20*mm, w - 20*mm])
dope_t.setStyle(TableStyle([
("BACKGROUND", (0,0), (0,-1), RED_ALERT),
("BACKGROUND", (1,0), (1,-1), RED_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),
("BOX", (0,0), (-1,-1), 1.5, RED_ALERT),
("LINEBELOW", (0,0), (-1,-1), 0.5, HexColor("#FFCDD2")),
("VALIGN", (0,0), (-1,-1), "MIDDLE"),
]))
story.append(KeepTogether([
step_box(1, "Call for Help — Stop Surgery"),
sp(1),
bullet("Inform surgical team, switch to 100% FiO2, manual ventilation"),
sp(2),
step_box(2, "Check Mechanical Causes — DOPE"),
sp(1),
Paragraph('<b>Always exclude mechanical causes BEFORE diagnosing bronchospasm!</b>',
ParagraphStyle("DI", fontName="Helvetica-Bold", fontSize=10, textColor=RED_ALERT,
leading=14, spaceAfter=4)),
dope_t,
sp(2),
]))
story.append(KeepTogether([
step_box(3, "Deepen Anesthesia"),
sp(1),
]))
deep_rows = [
["Agent", "Notes"],
["Sevoflurane", "Preferred volatile — bronchodilator, least irritant"],
["Isoflurane", "Bronchodilator — reasonable option"],
["Propofol", "IV agent — useful for deepening"],
["Ketamine", "<b>Drug of choice in asthmatics</b> — bronchodilator, sympathomimetic, releases catecholamines"],
]
d_data = []
for i, row in enumerate(deep_rows):
d_data.append([Paragraph(c, ParagraphStyle("DT",
fontName="Helvetica-Bold" if i == 0 else ("Helvetica-Bold" if c.startswith("<b>") else "Helvetica"),
fontSize=9.5, textColor=colors.white if i==0 else DARK_TEXT, leading=13)) for c in row])
w = PAGE_W - 36*mm
d_t = Table(d_data, colWidths=[w*0.25, w*0.75])
d_t.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,0), MEDIUM_BLUE),
("BACKGROUND", (0,2), (-1,2), LIGHT_BLUE),
("BACKGROUND", (0,4), (-1,4), LIGHT_BLUE),
("TOPPADDING", (0,0), (-1,-1), 4),
("BOTTOMPADDING", (0,0), (-1,-1), 4),
("LEFTPADDING", (0,0), (-1,-1), 6),
("RIGHTPADDING", (0,0), (-1,-1), 6),
("BOX", (0,0), (-1,-1), 1, MEDIUM_BLUE),
("LINEBELOW", (0,0), (-1,-1), 0.5, BORDER_GRAY),
("VALIGN", (0,0), (-1,-1), "MIDDLE"),
]))
story.append(d_t)
story.append(sp(3))
# Drug Management
story.append(section_header("PHARMACOLOGICAL MANAGEMENT"))
story.append(sp(2))
# Beta-2 Agonists
story.append(subsection_box("1. Beta-2 Agonists — First-Line Therapy", bg=GREEN_BG, fg=GREEN_DRUG))
story.append(sp(1))
b2_rows = [
["Drug", "Route", "Dose", "Frequency"],
["Salbutamol", "Nebulization", "2.5 mg", "Every 20 min x3, then 2–4 hourly"],
["Salbutamol", "MDI via ETT", "4–8 puffs", "Via MDI adapter in ETT"],
["Terbutaline", "SC", "0.25 mg", "Every 20 min, max 3 doses"],
["Albuterol / Levalbuterol / Pirbuterol", "Inhaled", "As per preparation", "Alternatives to salbutamol"],
]
b2_data = []
for i, row in enumerate(b2_rows):
b2_data.append([Paragraph(c, ParagraphStyle("BT",
fontName="Helvetica-Bold" if i == 0 else "Helvetica",
fontSize=9.5, textColor=colors.white if i==0 else DARK_TEXT, leading=13)) for c in row])
w = PAGE_W - 36*mm
b2_t = Table(b2_data, colWidths=[w*0.28, w*0.18, w*0.18, w*0.36])
b2_t.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,0), GREEN_DRUG),
("BACKGROUND", (0,2), (-1,2), GREEN_BG),
("BACKGROUND", (0,4), (-1,4), GREEN_BG),
("TOPPADDING", (0,0), (-1,-1), 4),
("BOTTOMPADDING", (0,0), (-1,-1), 4),
("LEFTPADDING", (0,0), (-1,-1), 5),
("RIGHTPADDING", (0,0), (-1,-1), 5),
("BOX", (0,0), (-1,-1), 1, GREEN_DRUG),
("LINEBELOW", (0,0), (-1,-1), 0.5, BORDER_GRAY),
("VALIGN", (0,0), (-1,-1), "MIDDLE"),
]))
story.append(b2_t)
story.append(sp(2))
# Anticholinergics
story.append(subsection_box("2. Anticholinergics"))
story.append(sp(1))
story.append(bullet("<b>Ipratropium bromide</b> — 0.5 mg nebulization every 30 min x3, then every 2–4 hours"))
story.append(sp(2))
# Corticosteroids
story.append(subsection_box("3. Corticosteroids"))
story.append(sp(1))
cs_rows = [
["Methylprednisolone", "125 mg IV bolus, then 40–60 mg IV every 6 hours"],
["Hydrocortisone", "200 mg IV every 4–6 hours"],
["Dexamethasone", "IV as adjunct"],
]
story.append(two_col_table(cs_rows))
story.append(sp(1))
story.append(Paragraph(
"<i>Important: Corticosteroids have a delayed onset of 4–6 hours. They do NOT provide immediate bronchodilation.</i>",
NOTE_STYLE))
story.append(sp(2))
# Epinephrine
story.append(subsection_box("4. Epinephrine"))
story.append(sp(1))
story.append(Paragraph("<b>Indications:</b> Severe bronchospasm, Anaphylaxis", BOLD_ITEM))
story.append(bullet("0.3 mL of 1:1000 IM"))
story.append(bullet("IV infusion: 0.5–2 mcg/min, titrated to response"))
story.append(sp(2))
# Magnesium
story.append(subsection_box("5. Magnesium Sulphate"))
story.append(sp(1))
story.append(bullet("<b>1–2 g IV</b> over 20 minutes"))
story.append(bullet("Useful in <b>refractory bronchospasm</b>"))
story.append(sp(2))
# Aminophylline
story.append(subsection_box("6. Aminophylline"))
story.append(sp(1))
story.append(bullet("Loading: <b>5 mg/kg IV slowly</b>"))
story.append(bullet("Maintenance infusion: <b>0.5–0.9 mg/kg/hr</b> (adjust to serum levels)"))
story.append(sp(2))
# Heliox
story.append(subsection_box("7. Heliox"))
story.append(sp(1))
story.append(bullet("Helium 70–79% + Oxygen 21–30%"))
story.append(bullet("Low-density gas — reduces turbulent airflow and airway resistance"))
story.append(bullet("Improves ventilation distribution and reduces work of breathing"))
story.append(sp(3))
# Ventilator Strategy
story.append(section_header("VENTILATOR STRATEGY IN BRONCHOSPASM"))
story.append(sp(2))
vent_strat = [
["Parameter", "Target / Strategy"],
["Respiratory Rate", "Low (8–12 breaths/min)"],
["Tidal Volume", "6–8 mL/kg"],
["Inspiratory Flow", "High (allows more time for expiration)"],
["I:E Ratio", "1:3 to 1:5 (prolonged expiratory time)"],
["Auto-PEEP", "Avoid — monitor end-expiratory flow"],
["Permissive Hypercapnia", "Accepted if necessary to avoid barotrauma"],
]
vs_data = []
for i, row in enumerate(vent_strat):
vs_data.append([Paragraph(c, ParagraphStyle("VS",
fontName="Helvetica-Bold" if i == 0 else "Helvetica",
fontSize=9.5, textColor=colors.white if i==0 else DARK_TEXT, leading=13)) for c in row])
w = PAGE_W - 36*mm
vs_t = Table(vs_data, colWidths=[w*0.35, w*0.65])
vs_t.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,0), MEDIUM_BLUE),
("BACKGROUND", (0,2), (-1,2), LIGHT_BLUE),
("BACKGROUND", (0,4), (-1,4), LIGHT_BLUE),
("BACKGROUND", (0,6), (-1,6), LIGHT_BLUE),
("TOPPADDING", (0,0), (-1,-1), 4),
("BOTTOMPADDING", (0,0), (-1,-1), 4),
("LEFTPADDING", (0,0), (-1,-1), 6),
("RIGHTPADDING", (0,0), (-1,-1), 6),
("BOX", (0,0), (-1,-1), 1, MEDIUM_BLUE),
("LINEBELOW", (0,0), (-1,-1), 0.5, BORDER_GRAY),
("VALIGN", (0,0), (-1,-1), "MIDDLE"),
]))
story.append(vs_t)
story.append(sp(3))
# Prevention
story.append(section_header("PREVENTION"))
story.append(sp(2))
prev_b = [
"Optimize asthma / COPD well before elective surgery",
"Continue preoperative bronchodilators on the day of surgery",
"Administer steroids in high-risk patients (begin 24–48 h preoperatively)",
"Prefer <b>LMA over ETT</b> when clinically appropriate",
"Maintain adequate depth of anesthesia throughout",
"Gentle airway manipulation and careful suctioning",
"Adequate multimodal analgesia (reduces reflex bronchospasm)",
"Deep extubation in selected patients",
"Incentive spirometry and early mobilization postoperatively",
]
for p_ in prev_b:
story.append(bullet(p_))
story.append(sp(3))
# Complications
story.append(section_header("COMPLICATIONS"))
story.append(sp(2))
comp_b = [
"Hypoxemia and hypercapnia", "Dynamic hyperinflation and auto-PEEP",
"Barotrauma and pneumothorax", "Hypotension (from dynamic hyperinflation or drug effects)",
"Cardiac arrest", "Respiratory failure requiring ICU admission"
]
w = PAGE_W - 36*mm
comp_pairs = [[
Paragraph(f"\u2022 {comp_b[i]}", BULLET),
Paragraph(f"\u2022 {comp_b[i+1]}" if i+1 < len(comp_b) else "", BULLET)
] for i in range(0, len(comp_b), 2)]
comp_t = Table(comp_pairs, colWidths=[w*0.5, w*0.5])
comp_t.setStyle(TableStyle([
("TOPPADDING", (0,0), (-1,-1), 3),
("BOTTOMPADDING", (0,0), (-1,-1), 3),
("LEFTPADDING", (0,0), (-1,-1), 4),
("RIGHTPADDING", (0,0), (-1,-1), 4),
("VALIGN", (0,0), (-1,-1), "TOP"),
]))
story.append(comp_t)
story.append(sp(3))
# VIVA – Bronchospasm
story.append(section_header("VIVA POINTS — INTRAOPERATIVE BRONCHOSPASM", bg=TEAL))
story.append(sp(2))
viva2 = [
("Most common cause (intraop):", "Airway irritation in light plane of anesthesia"),
("Most common cause (maintenance):", "Anaphylaxis"),
("DOC in asthmatics:", "Ketamine (bronchodilator, sympathomimetic)"),
("First-line bronchodilator:", "Salbutamol (Beta-2 agonist)"),
("Steroid onset:", "4–6 hours (no immediate relief)"),
("Magnesium dose:", "1–2 g IV over 20 min for refractory bronchospasm"),
("DOPE mnemonic:", "Displacement, Obstruction, Pneumothorax, Equipment failure"),
("Silent chest means:", "Severe life-threatening bronchospasm — immediate action required"),
("I:E ratio in bronchospasm:", "1:3 to 1:5 (prolonged expiration to prevent air trapping)"),
("Permissive hypercapnia:", "Acceptable strategy to prevent barotrauma in severe obstruction"),
("Avoid in bronchospasm:", "Desflurane (most irritant), Morphine, Beta-blockers, Neostigmine"),
]
story.append(viva_box(viva2))
story.append(sp(4))
# ── COMPARISON TABLE ────────────────────────────────────────────
story.append(PageBreak())
story.append(section_header("COMPARISON — LARYNGOSPASM vs BRONCHOSPASM", bg=PURPLE))
story.append(sp(3))
comp_hdr = ["Feature", "Laryngospasm", "Bronchospasm"]
comp_rows2 = [
["Site", "Upper airway (glottis / vocal cords)", "Lower airway (bronchial smooth muscle)"],
["Mechanism", "Reflex adductor muscle contraction", "Smooth muscle contraction"],
["Sound", "Inspiratory stridor (or silent if complete)", "Expiratory wheeze"],
["SpO2 pattern", "Rapid, severe desaturation", "Gradual desaturation"],
["Peak airway pressure", "Less affected", "Markedly elevated"],
["Drug of choice", "Succinylcholine", "Salbutamol (+ Ketamine in asthmatics)"],
["Deepening agent", "Propofol 1–2 mg/kg", "Ketamine / Sevoflurane"],
["Key maneuver", "Larson's Maneuver", "DOPE check"],
["CPAP role", "CPAP 20–40 cm H2O", "Prolonged expiration; avoid auto-PEEP"],
["Main complication", "Negative Pressure Pulmonary Edema", "Auto-PEEP, Barotrauma, Pneumothorax"],
["Preferred volatile", "Sevoflurane (least irritant)", "Sevoflurane / Isoflurane"],
["Avoid", "Ether, Desflurane, Morphine, Barbiturates", "Desflurane, Morphine, Beta-blockers, Neostigmine"],
]
all_rows = [comp_hdr] + comp_rows2
tbl_data2 = []
for i, row in enumerate(all_rows):
tbl_data2.append([
Paragraph(f"<b>{row[0]}</b>", ParagraphStyle("CFH", fontName="Helvetica-Bold",
fontSize=9.5, textColor=colors.white if i==0 else DARK_NAVY, leading=13)),
Paragraph(row[1], ParagraphStyle("CFB", fontName="Helvetica", fontSize=9.5,
textColor=colors.white if i==0 else DARK_TEXT, leading=13)),
Paragraph(row[2], ParagraphStyle("CFC", fontName="Helvetica", fontSize=9.5,
textColor=colors.white if i==0 else DARK_TEXT, leading=13)),
])
w = PAGE_W - 36*mm
comp_t2 = Table(tbl_data2, colWidths=[w*0.25, w*0.375, w*0.375])
comp_style = [
("BACKGROUND", (0,0), (-1,0), PURPLE),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING", (0,0), (-1,-1), 5),
("LEFTPADDING", (0,0), (-1,-1), 6),
("RIGHTPADDING", (0,0), (-1,-1), 6),
("BOX", (0,0), (-1,-1), 1, PURPLE),
("LINEBELOW", (0,0), (-1,-1), 0.5, BORDER_GRAY),
("VALIGN", (0,0), (-1,-1), "TOP"),
]
for i in range(1, len(tbl_data2), 2):
comp_style.append(("BACKGROUND", (0,i), (-1,i), PURPLE_BG))
comp_t2.setStyle(TableStyle(comp_style))
story.append(comp_t2)
story.append(sp(6))
# Footer note
story.append(Paragraph(
"These notes are compiled for examination preparation. Always refer to current institutional "
"protocols and clinical guidelines for patient management decisions.",
ParagraphStyle("Footer", fontName="Helvetica-Oblique", fontSize=8.5, textColor=MID_TEXT,
leading=12, alignment=TA_CENTER)
))
# ── BUILD ────────────────────────────────────────────────────────
doc.build(story)
print("PDF generated successfully.")
cd /home/daytona/workspace/anesthesia-notes && python generate_notes.py 2>&1
ls -lh /home/daytona/workspace/anesthesia-notes/Airway_Emergencies_Notes.pdf
Airway Emergencies Notes
PDF Document · PDF
Make proper alined pdf for exam
~/anesthesia-notes/exam_notes.py
from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.units import mm
from reportlab.lib.styles import ParagraphStyle
from reportlab.lib.enums import TA_LEFT, TA_CENTER, TA_JUSTIFY
from reportlab.platypus import (
SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
HRFlowable, PageBreak, KeepTogether
)
from reportlab.lib.colors import HexColor
from reportlab.platypus import Frame, PageTemplate
from reportlab.platypus.doctemplate import BaseDocTemplate
# ────────────────────────────────────────────────
# COLORS
# ────────────────────────────────────────────────
C_NAVY = HexColor("#1B2A4A")
C_BLUE = HexColor("#1F5C99")
C_LTBLUE = HexColor("#E8F1FB")
C_MIDBLUE = HexColor("#C5D9F1")
C_TEAL = HexColor("#0D6E6E")
C_LTEAL = HexColor("#DDF4F2")
C_ORANGE = HexColor("#B34E00")
C_LTORANGE = HexColor("#FFF0E5")
C_RED = HexColor("#990000")
C_LTRED = HexColor("#FFF0F0")
C_GREEN = HexColor("#1A5C2A")
C_LTGREEN = HexColor("#EBF7ED")
C_PURPLE = HexColor("#3D1B8C")
C_LTPURPLE = HexColor("#F0EBFF")
C_GRAY = HexColor("#F4F4F4")
C_DGRAY = HexColor("#666666")
C_BLACK = HexColor("#1A1A1A")
C_WHITE = colors.white
C_BORDER = HexColor("#CCCCCC")
PW, PH = A4
USABLE = PW - 30*mm # 180mm usable width (15mm each side)
# ────────────────────────────────────────────────
# DOCUMENT
# ────────────────────────────────────────────────
doc = SimpleDocTemplate(
"/home/daytona/workspace/anesthesia-notes/Airway_Emergencies_EXAM.pdf",
pagesize=A4,
leftMargin=15*mm, rightMargin=15*mm,
topMargin=15*mm, bottomMargin=15*mm,
title="Airway Emergencies – Exam Notes",
)
story = []
# ────────────────────────────────────────────────
# STYLE FACTORY
# ────────────────────────────────────────────────
def ps(name, **kw):
return ParagraphStyle(name, **kw)
# Base text styles
BODY = ps("Body", fontName="Helvetica", fontSize=9.5, textColor=C_BLACK, leading=14, spaceAfter=2, alignment=TA_JUSTIFY)
BODYL = ps("BodyL", fontName="Helvetica", fontSize=9.5, textColor=C_BLACK, leading=14, spaceAfter=2, alignment=TA_LEFT)
BOLDS = ps("BoldS", fontName="Helvetica-Bold", fontSize=9.5, textColor=C_NAVY, leading=14, spaceAfter=2)
SMALL = ps("Small", fontName="Helvetica", fontSize=8.5, textColor=C_DGRAY, leading=12, spaceAfter=2, fontStyle="italic")
NOTE = ps("Note", fontName="Helvetica-Oblique", fontSize=9, textColor=C_DGRAY, leading=13, spaceAfter=3, leftIndent=8)
# Bullet styles (consistent left indent, hanging indent)
B1 = ps("B1", fontName="Helvetica", fontSize=9.5, textColor=C_BLACK, leading=14,
spaceAfter=2, leftIndent=14, firstLineIndent=-10)
B2 = ps("B2", fontName="Helvetica", fontSize=9, textColor=C_BLACK, leading=13,
spaceAfter=2, leftIndent=26, firstLineIndent=-10)
BG = ps("BG", fontName="Helvetica-Bold", fontSize=9.5, textColor=C_GREEN, leading=14,
spaceAfter=2, leftIndent=14, firstLineIndent=-10)
BR = ps("BR", fontName="Helvetica-Bold", fontSize=9.5, textColor=C_RED, leading=14,
spaceAfter=2, leftIndent=14, firstLineIndent=-10)
BO = ps("BO", fontName="Helvetica-Bold", fontSize=9.5, textColor=C_ORANGE,leading=14,
spaceAfter=2, leftIndent=14, firstLineIndent=-10)
# Table cell styles
TH = ps("TH", fontName="Helvetica-Bold", fontSize=9.5, textColor=C_WHITE, leading=13)
TC = ps("TC", fontName="Helvetica", fontSize=9.5, textColor=C_BLACK, leading=13)
TCB = ps("TCB", fontName="Helvetica-Bold", fontSize=9.5, textColor=C_NAVY, leading=13)
TCS = ps("TCS", fontName="Helvetica", fontSize=9, textColor=C_BLACK, leading=12)
# ────────────────────────────────────────────────
# HELPER FUNCTIONS
# ────────────────────────────────────────────────
def sp(h=3):
return Spacer(1, h*mm)
def hr(col=C_BORDER, thick=0.5):
return HRFlowable(width="100%", thickness=thick, color=col, spaceBefore=2, spaceAfter=2)
def p(text, style=BODY):
return Paragraph(text, style)
def b1(text, bold_start=False):
return Paragraph(f"\u2022 {text}", B1)
def b2(text):
return Paragraph(f"\u2013 {text}", B2)
def b_red(text):
return Paragraph(f"\u2022 {text}", BR)
def b_green(text):
return Paragraph(f"\u2022 {text}", BG)
def b_orange(text):
return Paragraph(f"\u2022 {text}", BO)
# ── Section Headers ──────────────────────────────
def sec_head(text, bg=C_NAVY, fg=C_WHITE, size=11):
"""Full-width dark section header bar."""
styl = ps(f"SH_{text[:6]}", fontName="Helvetica-Bold", fontSize=size,
textColor=fg, leading=size+5, spaceBefore=0, spaceAfter=0)
t = Table([[Paragraph(text, styl)]], colWidths=[USABLE])
t.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), bg),
("TOPPADDING", (0,0), (-1,-1), 6),
("BOTTOMPADDING", (0,0), (-1,-1), 6),
("LEFTPADDING", (0,0), (-1,-1), 10),
("RIGHTPADDING", (0,0), (-1,-1), 10),
]))
return t
def sub_head(text, bg=C_LTBLUE, fg=C_NAVY, size=10):
"""Sub-section header (lighter)."""
styl = ps(f"SSH_{text[:6]}", fontName="Helvetica-Bold", fontSize=size,
textColor=fg, leading=size+4, spaceBefore=0, spaceAfter=0)
t = Table([[Paragraph(text, styl)]], colWidths=[USABLE])
t.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), bg),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING", (0,0), (-1,-1), 5),
("LEFTPADDING", (0,0), (-1,-1), 8),
("RIGHTPADDING", (0,0), (-1,-1), 8),
("LINEBELOW", (0,0), (-1,-1), 1, fg),
]))
return t
def step_head(num, title, bg=C_LTORANGE, fg=C_ORANGE):
styl = ps(f"ST_{num}", fontName="Helvetica-Bold", fontSize=10,
textColor=fg, leading=15)
label_styl = ps(f"SL_{num}", fontName="Helvetica-Bold", fontSize=12,
textColor=fg, leading=15, alignment=TA_CENTER)
t = Table([[Paragraph(f"STEP {num}", label_styl),
Paragraph(title, styl)]],
colWidths=[18*mm, USABLE - 18*mm])
t.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), bg),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING", (0,0), (-1,-1), 5),
("LEFTPADDING", (0,0), (-1,-1), 6),
("RIGHTPADDING", (0,0), (-1,-1), 6),
("LINEBELOW", (0,0), (-1,-1), 1.2, fg),
("VALIGN", (0,0), (-1,-1), "MIDDLE"),
("LINEBEFORE", (1,0), (1,-1), 1.2, fg),
]))
return t
def alert(text):
"""Red warning box."""
styl = ps("ALT", fontName="Helvetica-Bold", fontSize=9.5,
textColor=C_RED, leading=14)
t = Table([[Paragraph(f"\u26a0 {text}", styl)]], colWidths=[USABLE])
t.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), C_LTRED),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING", (0,0), (-1,-1), 5),
("LEFTPADDING", (0,0), (-1,-1), 8),
("RIGHTPADDING", (0,0), (-1,-1), 8),
("BOX", (0,0), (-1,-1), 1, C_RED),
]))
return t
def info_box(text, bg=C_LTBLUE, fg=C_NAVY):
styl = ps("IB", fontName="Helvetica", fontSize=9.5,
textColor=C_BLACK, leading=14, alignment=TA_JUSTIFY)
t = Table([[Paragraph(text, styl)]], colWidths=[USABLE])
t.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), bg),
("TOPPADDING", (0,0), (-1,-1), 6),
("BOTTOMPADDING", (0,0), (-1,-1), 6),
("LEFTPADDING", (0,0), (-1,-1), 8),
("RIGHTPADDING", (0,0), (-1,-1), 8),
("BOX", (0,0), (-1,-1), 1, fg),
]))
return t
def two_col(rows, w1=0.35, hdr=None, alt_bg=C_GRAY):
"""Simple two-column table. rows = list of (left_str, right_str)."""
c1, c2 = USABLE * w1, USABLE * (1 - w1)
data = []
if hdr:
data.append([Paragraph(f"<b>{hdr[0]}</b>", TH),
Paragraph(f"<b>{hdr[1]}</b>", TH)])
for row in rows:
data.append([Paragraph(row[0], TCB), Paragraph(row[1], TC)])
style = [
("TOPPADDING", (0,0), (-1,-1), 4),
("BOTTOMPADDING", (0,0), (-1,-1), 4),
("LEFTPADDING", (0,0), (-1,-1), 6),
("RIGHTPADDING", (0,0), (-1,-1), 6),
("BOX", (0,0), (-1,-1), 0.8, C_BORDER),
("LINEBELOW", (0,0), (-1,-1), 0.4, C_BORDER),
("VALIGN", (0,0), (-1,-1), "TOP"),
]
if hdr:
style.append(("BACKGROUND", (0,0), (-1,0), C_NAVY))
for i in range(1 if hdr else 0, len(data), 2):
style.append(("BACKGROUND", (0,i), (-1,i), alt_bg))
t = Table(data, colWidths=[c1, c2])
t.setStyle(TableStyle(style))
return t
def multi_col(rows_data, col_widths, hdr=True, header_bg=C_NAVY):
"""Multi-column table."""
data = []
for i, row in enumerate(rows_data):
if i == 0 and hdr:
data.append([Paragraph(f"<b>{c}</b>", TH) for c in row])
else:
data.append([Paragraph(str(c), TC) for c in row])
style = [
("TOPPADDING", (0,0), (-1,-1), 4),
("BOTTOMPADDING", (0,0), (-1,-1), 4),
("LEFTPADDING", (0,0), (-1,-1), 6),
("RIGHTPADDING", (0,0), (-1,-1), 6),
("BOX", (0,0), (-1,-1), 0.8, C_BORDER),
("LINEBELOW", (0,0), (-1,-1), 0.4, C_BORDER),
("VALIGN", (0,0), (-1,-1), "TOP"),
]
if hdr:
style.append(("BACKGROUND", (0,0), (-1,0), header_bg))
for i in range(1, len(data), 2):
style.append(("BACKGROUND", (0,i), (-1,i), C_GRAY))
t = Table(data, colWidths=col_widths)
t.setStyle(TableStyle(style))
return t
def pathway(steps):
"""Vertical pathway diagram in a single table."""
rows = []
for i, step in enumerate(steps):
cell_style = ps(f"PC{i}", fontName="Helvetica", fontSize=9.5,
textColor=C_BLACK, leading=14, alignment=TA_CENTER)
rows.append([Paragraph(step, cell_style)])
if i < len(steps) - 1:
arr_style = ps(f"PA{i}", fontName="Helvetica-Bold", fontSize=14,
textColor=C_PURPLE, leading=16, alignment=TA_CENTER)
rows.append([Paragraph("▼", arr_style)])
style = [
("TOPPADDING", (0,0), (-1,-1), 4),
("BOTTOMPADDING", (0,0), (-1,-1), 4),
("LEFTPADDING", (0,0), (-1,-1), 6),
("RIGHTPADDING", (0,0), (-1,-1), 6),
("ALIGN", (0,0), (-1,-1), "CENTER"),
("VALIGN", (0,0), (-1,-1), "MIDDLE"),
]
for i in range(0, len(rows), 2):
style.append(("BACKGROUND", (0,i), (-1,i), C_LTPURPLE))
style.append(("BOX", (0,i), (-1,i), 0.6, C_PURPLE))
t = Table(rows, colWidths=[USABLE * 0.7])
t.setStyle(TableStyle(style))
# Center the table using outer wrapper
wrapper = Table([[t]], colWidths=[USABLE])
wrapper.setStyle(TableStyle([
("ALIGN", (0,0), (-1,-1), "CENTER"),
("TOPPADDING", (0,0), (-1,-1), 0),
("BOTTOMPADDING", (0,0), (-1,-1), 0),
("LEFTPADDING", (0,0), (-1,-1), 0),
("RIGHTPADDING", (0,0), (-1,-1), 0),
]))
return wrapper
def viva_table(rows):
"""Viva Q&A style table."""
data = []
for row in rows:
data.append([
Paragraph(f"<b>{row[0]}</b>", ps("VK", fontName="Helvetica-Bold", fontSize=9.5,
textColor=C_TEAL, leading=13)),
Paragraph(row[1], ps("VV", fontName="Helvetica", fontSize=9.5,
textColor=C_BLACK, leading=13))
])
c1 = USABLE * 0.38
c2 = USABLE * 0.62
t = Table(data, colWidths=[c1, c2])
t.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), C_LTEAL),
("ROWBACKGROUNDS",(0,0), (-1,-1), [C_LTEAL, HexColor("#D4EDE9")]),
("TOPPADDING", (0,0), (-1,-1), 4),
("BOTTOMPADDING", (0,0), (-1,-1), 4),
("LEFTPADDING", (0,0), (-1,-1), 6),
("RIGHTPADDING", (0,0), (-1,-1), 6),
("BOX", (0,0), (-1,-1), 1, C_TEAL),
("LINEBELOW", (0,0), (-1,-1), 0.4, HexColor("#A3CEC9")),
("VALIGN", (0,0), (-1,-1), "TOP"),
]))
return t
def dope_box():
rows = [
("D", "isplacement of ETT"),
("O", "bstruction — secretions, kink, mucus plug"),
("P", "neumothorax"),
("E", "quipment failure"),
]
data = []
for letter, rest in rows:
data.append([
Paragraph(f"<b>{letter}</b>", ps("DL", fontName="Helvetica-Bold", fontSize=18,
textColor=C_WHITE, leading=22, alignment=TA_CENTER)),
Paragraph(f"<b>{letter}</b>{rest}", ps("DR", fontName="Helvetica", fontSize=10,
textColor=C_BLACK, leading=14))
])
t = Table(data, colWidths=[14*mm, USABLE - 14*mm])
t.setStyle(TableStyle([
("BACKGROUND", (0,0), (0,-1), C_RED),
("BACKGROUND", (1,0), (1,-1), C_LTRED),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING", (0,0), (-1,-1), 5),
("LEFTPADDING", (0,0), (-1,-1), 5),
("RIGHTPADDING", (0,0), (-1,-1), 8),
("BOX", (0,0), (-1,-1), 1.2, C_RED),
("LINEBELOW", (0,0), (-1,-1), 0.5, HexColor("#FFCCCC")),
("VALIGN", (0,0), (-1,-1), "MIDDLE"),
]))
return t
# ══════════════════════════════════════════════════════════════════
# COVER PAGE
# ══════════════════════════════════════════════════════════════════
def cover_page():
elems = []
# Top decorative band
band = Table([[""]], colWidths=[USABLE])
band.setStyle(TableStyle([("BACKGROUND", (0,0), (-1,-1), C_NAVY),
("TOPPADDING", (0,0), (-1,-1), 6),
("BOTTOMPADDING", (0,0), (-1,-1), 6)]))
elems.append(band)
elems.append(sp(12))
# Title block
title_styl = ps("CVT", fontName="Helvetica-Bold", fontSize=26, textColor=C_NAVY,
leading=32, alignment=TA_CENTER, spaceAfter=4)
sub_styl = ps("CVS", fontName="Helvetica", fontSize=13, textColor=C_BLUE,
leading=18, alignment=TA_CENTER, spaceAfter=6)
tag_styl = ps("CVG", fontName="Helvetica-Oblique", fontSize=10.5, textColor=C_DGRAY,
leading=15, alignment=TA_CENTER)
elems.append(p("ANESTHESIA EXAM NOTES", sub_styl))
elems.append(p("AIRWAY EMERGENCIES", title_styl))
elems.append(hr(C_NAVY, 2))
elems.append(sp(2))
elems.append(p("Laryngospasm | Intraoperative Bronchospasm", sub_styl))
elems.append(sp(2))
elems.append(p("Comprehensive notes covering Etiology \u2022 Pathophysiology \u2022 "
"Clinical Features \u2022 Management \u2022 Prevention \u2022 Viva Points",
tag_styl))
elems.append(sp(8))
# Quick-reference comparison box
elems.append(sub_head("QUICK REFERENCE — AT A GLANCE", bg=C_MIDBLUE, fg=C_NAVY))
elems.append(sp(2))
qr = [
["Feature", "Laryngospasm", "Bronchospasm"],
["Site", "Upper airway (vocal cords)", "Lower airway (bronchi)"],
["Sound", "Inspiratory stridor / Silent chest", "Expiratory wheeze / Silent chest"],
["Drug of Choice", "Succinylcholine", "Salbutamol (+ Ketamine in asthma)"],
["Deepen with", "Propofol 1–2 mg/kg IV", "Ketamine / Sevoflurane"],
["Special Technique", "Larson's Maneuver", "DOPE Check"],
["CPAP target", "20–40 cm H\u2082O", "Prolonged expiration; avoid auto-PEEP"],
["Main Complication", "Neg. Pressure Pulmonary Edema", "Auto-PEEP, Barotrauma, Pneumothorax"],
]
qr_data = []
for i, row in enumerate(qr):
if i == 0:
qr_data.append([Paragraph(f"<b>{c}</b>", TH) for c in row])
else:
qr_data.append([Paragraph(row[0], TCB),
Paragraph(row[1], TC),
Paragraph(row[2], TC)])
c1, c2, c3 = USABLE*0.22, USABLE*0.39, USABLE*0.39
qr_t = Table(qr_data, colWidths=[c1, c2, c3])
qr_t.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,0), C_NAVY),
("TOPPADDING", (0,0), (-1,-1), 4),
("BOTTOMPADDING", (0,0), (-1,-1), 4),
("LEFTPADDING", (0,0), (-1,-1), 6),
("RIGHTPADDING", (0,0), (-1,-1), 6),
("BOX", (0,0), (-1,-1), 0.8, C_NAVY),
("LINEBELOW", (0,0), (-1,-1), 0.4, C_BORDER),
("ROWBACKGROUNDS",(0,1), (-1,-1), [C_WHITE, C_GRAY]),
("VALIGN", (0,0), (-1,-1), "MIDDLE"),
]))
elems.append(qr_t)
elems.append(sp(6))
# Contents list
elems.append(sub_head("CONTENTS", bg=C_LTBLUE, fg=C_NAVY))
elems.append(sp(2))
contents = [
("Part I", "LARYNGOSPASM",
"Definition \u2022 Etiology \u2022 Pathophysiology \u2022 Clinical Features \u2022 Management \u2022 Prevention \u2022 Viva Points"),
("Part II", "INTRAOPERATIVE BRONCHOSPASM",
"Definition \u2022 Etiology \u2022 Pathophysiology \u2022 Clinical Features \u2022 Management \u2022 Ventilator Strategy \u2022 Viva Points"),
("Part III","COMPARISON TABLE",
"Side-by-side comparison of key features"),
]
cnt_data = []
for part, title, detail in contents:
cnt_data.append([
Paragraph(f"<b>{part}</b>", ps("CP", fontName="Helvetica-Bold", fontSize=9.5,
textColor=C_BLUE, leading=13, alignment=TA_CENTER)),
Paragraph(f"<b>{title}</b>", ps("CT", fontName="Helvetica-Bold", fontSize=9.5,
textColor=C_NAVY, leading=13)),
Paragraph(detail, ps("CD", fontName="Helvetica", fontSize=9,
textColor=C_DGRAY, leading=12)),
])
cnt_t = Table(cnt_data, colWidths=[USABLE*0.12, USABLE*0.30, USABLE*0.58])
cnt_t.setStyle(TableStyle([
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING", (0,0), (-1,-1), 5),
("LEFTPADDING", (0,0), (-1,-1), 6),
("RIGHTPADDING", (0,0), (-1,-1), 6),
("BOX", (0,0), (-1,-1), 0.8, C_BORDER),
("LINEBELOW", (0,0), (-1,-1), 0.4, C_BORDER),
("ROWBACKGROUNDS",(0,0), (-1,-1), [C_WHITE, C_GRAY]),
("VALIGN", (0,0), (-1,-1), "MIDDLE"),
]))
elems.append(cnt_t)
elems.append(PageBreak())
return elems
# ══════════════════════════════════════════════════════════════════
# PART I — LARYNGOSPASM
# ══════════════════════════════════════════════════════════════════
def part1_laryngospasm():
elems = []
# Chapter banner
ch = Table([[Paragraph("PART I", ps("CHS", fontName="Helvetica",
fontSize=11, textColor=HexColor("#90CAF9"), leading=14)),
Paragraph("LARYNGOSPASM", ps("CHT", fontName="Helvetica-Bold",
fontSize=18, textColor=C_WHITE, leading=22))]],
colWidths=[22*mm, USABLE - 22*mm])
ch.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), C_NAVY),
("TOPPADDING", (0,0), (-1,-1), 10),
("BOTTOMPADDING", (0,0), (-1,-1), 10),
("LEFTPADDING", (0,0), (-1,-1), 10),
("RIGHTPADDING", (0,0), (-1,-1), 10),
("VALIGN", (0,0), (-1,-1), "MIDDLE"),
("LINEBEFORE", (1,0), (1,-1), 2, C_BLUE),
]))
elems.append(ch)
elems.append(sp(4))
# ── Definition ───────────────────────────────────────
elems.append(sec_head("DEFINITION", bg=C_NAVY))
elems.append(sp(2))
elems.append(info_box(
"Laryngospasm is an <b>exaggerated protective glottic closure reflex</b> that causes "
"<b>forceful, sustained closure of the vocal cords</b>, persisting even after the "
"initiating stimulus is removed, resulting in <b>partial or complete upper airway obstruction</b>.",
bg=C_LTBLUE, fg=C_NAVY))
elems.append(sp(4))
# ── Etiology ─────────────────────────────────────────
elems.append(sec_head("ETIOLOGY", bg=C_NAVY))
elems.append(sp(2))
# Patient + Surgery side by side
left_items = [
("<b>A. PATIENT FACTORS</b>", ""),
("Pediatric age group", "(most common)"),
("Upper respiratory tract infection (URTI)", ""),
("Chronic smokers", ""),
("Reactive airway disease", ""),
]
right_items = [
("<b>B. SURGERY-RELATED</b>", ""),
("ENT surgeries", "tonsillectomy, adenoidectomy"),
("Airway procedures / Endoscopy", ""),
("Esophagoscopy", ""),
("Upper abdominal / Pelvic / Thoracic surgeries", ""),
]
# Use two parallel mini-tables
def mini_list(items, bg):
rows = []
for item in items:
rows.append([Paragraph(item[0], TCB if item[1] == "" and item[0].startswith("<b>") else
ps("ML", fontName="Helvetica", fontSize=9.5, textColor=C_BLACK, leading=13)),
Paragraph(item[1], ps("MR", fontName="Helvetica-Oblique", fontSize=9,
textColor=C_DGRAY, leading=13))])
t = Table(rows, colWidths=[(USABLE/2 - 4*mm)*0.65, (USABLE/2 - 4*mm)*0.35])
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), 5),
("RIGHTPADDING", (0,0), (-1,-1), 5),
("BOX", (0,0), (-1,-1), 0.6, C_BORDER),
("LINEBELOW", (0,1), (-1,-1), 0.3, C_BORDER),
("VALIGN", (0,0), (-1,-1), "TOP"),
]))
return t
side_t = Table([[mini_list(left_items, C_LTBLUE),
mini_list(right_items, HexColor("#F0F8FF"))]],
colWidths=[USABLE/2 - 2*mm, USABLE/2 - 2*mm],
hAlign="LEFT")
side_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), 3),
("VALIGN", (0,0), (-1,-1), "TOP"),
]))
elems.append(side_t)
elems.append(sp(2))
# Anesthetic factors
elems.append(sub_head("C. Anesthetic Factors", bg=C_LTBLUE, fg=C_NAVY))
elems.append(sp(1))
anesth_rows = [
["Factor", "Details"],
["Light plane of anesthesia", "<b>Most common intraoperative cause</b>"],
["Morphine", "Histamine release, vagal stimulation"],
["Barbiturates", "Increase parasympathetic activity"],
["Volatile irritancy order", "Ether > Isoflurane > Halothane > Enflurane | Sevoflurane = least irritant"],
["Airway irritants", "Secretions, blood, vomitus, suctioning, NG tube, oropharyngeal/nasal airways"],
]
elems.append(two_col(anesth_rows[1:], hdr=anesth_rows[0], w1=0.35, alt_bg=C_GRAY))
elems.append(sp(2))
# Postoperative causes
elems.append(sub_head("D. Postoperative Causes", bg=C_LTBLUE, fg=C_NAVY))
elems.append(sp(1))
po_rows = [
["Timing", "Causes"],
["<b>Immediate</b>", "Extubation during light anesthesia (especially post-halothane)"],
["<b>Delayed</b>", "Secretions around cords, regurgitated gastric contents, silent aspiration beside NG tube, "
"oropharyngeal airway irritation, hypoxia causing vomiting, epiglottitis"],
]
elems.append(two_col(po_rows[1:], hdr=po_rows[0], w1=0.20, alt_bg=C_GRAY))
elems.append(sp(4))
# ── Pathophysiology ──────────────────────────────────
elems.append(sec_head("PATHOPHYSIOLOGY", bg=C_NAVY))
elems.append(sp(3))
elems.append(pathway([
"Stimulus (secretions / blood / light anesthesia / airway manipulation)",
"Superior Laryngeal Nerve Activation",
"Vagus Nerve \u2192 Reflex Arc",
"Reflex Contraction of Laryngeal Adductor Muscles",
"Complete / Partial Glottic Closure \u2192 Airway Obstruction",
"Hypoxia + Hypercarbia",
"Forceful Inspiratory Effort Against Closed Glottis",
"Extreme Negative Intrathoracic Pressure",
"Negative Pressure Pulmonary Edema (NPPE)",
]))
elems.append(sp(4))
# ── Clinical Features ────────────────────────────────
elems.append(sec_head("CLINICAL FEATURES", bg=C_NAVY))
elems.append(sp(2))
cf = [
["Stage", "Signs & Symptoms"],
["<b>Early</b>", "Inspiratory stridor, Dyspnea, Tachypnea, Anxiety, Use of accessory muscles"],
["<b>Progressive Obstruction</b>",
"Intercostal retractions, Suprasternal indrawing, Tracheal tug, "
"Paradoxical chest-abdomen movements\n(chest sinks \u2193 while abdomen rises \u2191 on inspiration)"],
["<b>Complete Laryngospasm</b>",
"<b>Silent chest \u2022 No air entry \u2022 Rapid desaturation \u2022 Cyanosis \u2022 "
"Bradycardia (children) \u2022 Cardiac arrest if untreated</b>"],
]
cf_data = []
for i, row in enumerate(cf):
if i == 0:
cf_data.append([Paragraph(f"<b>{c}</b>", TH) for c in row])
elif i == 3:
cf_data.append([Paragraph(row[0], ps("CK3", fontName="Helvetica-Bold", fontSize=9.5,
textColor=C_RED, leading=13)),
Paragraph(row[1], ps("CV3", fontName="Helvetica-Bold", fontSize=9.5,
textColor=C_RED, leading=13))])
else:
cf_data.append([Paragraph(row[0], TCB), Paragraph(row[1], TC)])
cf_t = Table(cf_data, colWidths=[USABLE*0.25, USABLE*0.75])
cf_t.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,0), C_NAVY),
("BACKGROUND", (0,1), (-1,1), C_GRAY),
("BACKGROUND", (0,2), (-1,2), C_LTBLUE),
("BACKGROUND", (0,3), (-1,3), C_LTRED),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING", (0,0), (-1,-1), 5),
("LEFTPADDING", (0,0), (-1,-1), 6),
("RIGHTPADDING", (0,0), (-1,-1), 6),
("BOX", (0,0), (-1,-1), 0.8, C_NAVY),
("LINEBELOW", (0,0), (-1,-1), 0.4, C_BORDER),
("VALIGN", (0,0), (-1,-1), "MIDDLE"),
]))
elems.append(cf_t)
elems.append(sp(4))
# ── Management ───────────────────────────────────────
elems.append(sec_head("MANAGEMENT", bg=C_NAVY))
elems.append(sp(2))
# Step 1
elems.append(KeepTogether([
step_head("1", "CALL FOR HELP"),
sp(1),
b1("Stop surgery immediately and inform the surgeon"),
b1("Administer <b>100% oxygen</b> via face mask"),
sp(2),
]))
# Step 2
elems.append(KeepTogether([
step_head("2", "REMOVE THE STIMULUS"),
sp(1),
b1("Stop all surgical stimulation"),
b1("Suction blood, secretions from oropharynx and airway"),
b1("Remove airway irritants: NG tube, oropharyngeal/nasal airways if indicated"),
sp(2),
]))
# Step 3
elems.append(KeepTogether([
step_head("3", "DEEPEN ANESTHESIA"),
sp(1),
b1("<b>Propofol 1–2 mg/kg IV</b> — preferred drug for deepening"),
b1("Volatile: <b>Sevoflurane</b> preferred (least irritant); avoid ether / desflurane"),
sp(2),
]))
# Step 4
elems.append(KeepTogether([
step_head("4", "OPEN THE AIRWAY"),
sp(1),
b1("Head extension \u2022 Jaw thrust \u2022 Chin lift"),
b1("<b>Larson's Maneuver</b> \u2014 apply firm bilateral pressure in the <b>laryngospasm notch</b>:"),
b2("Located just posterior to the angle of the mandible, anterior to the mastoid process"),
b2("Painful periosteal stimulation reflexively breaks the spasm"),
sp(2),
]))
# Step 5
elems.append(KeepTogether([
step_head("5", "POSITIVE PRESSURE VENTILATION"),
sp(1),
b1("Apply <b>tight-fitting face mask</b> with 100% oxygen"),
b1("<b>CPAP 20\u201340 cm H\u2082O</b> \u2014 may break partial spasm"),
alert("Do NOT apply excessive pressure in complete obstruction: causes gastric insufflation and aspiration risk"),
sp(2),
]))
# Step 6 — Drugs
elems.append(step_head("6", "PHARMACOLOGICAL MANAGEMENT"))
elems.append(sp(1))
elems.append(sub_head("Succinylcholine — Drug of Choice", bg=C_LTGREEN, fg=C_GREEN))
elems.append(sp(1))
sux = [
["Route", "Dose", "Purpose"],
["IV (low dose)", "0.1\u20130.5 mg/kg", "Break spasm; maintain spontaneous breathing"],
["IV (full dose)", "1\u20132 mg/kg", "Rapid paralysis + endotracheal intubation"],
["IM", "4\u20135 mg/kg", "No IV access"],
["Intralingual / IO", "Same as IM dose", "Emergency alternative routes"],
]
elems.append(multi_col(sux,
[USABLE*0.22, USABLE*0.22, USABLE*0.56],
header_bg=C_GREEN))
elems.append(sp(1))
elems.append(p("<i>Always ventilate with 100% oxygen after succinylcholine.</i>", NOTE))
elems.append(sp(2))
# Step 7
elems.append(KeepTogether([
step_head("7", "SECURE THE AIRWAY"),
sp(1),
b1("If obstruction persists \u2192 <b>Endotracheal intubation</b>"),
b1("If cannot intubate \u2192 <b>Surgical airway</b> (cricothyrotomy / tracheostomy)"),
b1("Do <b>NOT</b> force ETT against closed cords"),
sp(3),
]))
# ── Adjunctive Therapy ───────────────────────────────
elems.append(sec_head("ADJUNCTIVE THERAPY", bg=C_NAVY))
elems.append(sp(2))
adj = [
["Agent", "Dose / Preparation", "Action"],
["<b>Heliox</b>", "70% Helium + 30% O\u2082", "Low-density gas; reduces turbulent flow; decreases airway resistance; improves tidal volume"],
["<b>Racemic Epinephrine</b>", "1 mg of 1:1000 in 5 mL NS; nebulized every 30 min", "Vasoconstriction; reduces airway mucosal edema"],
["<b>Dexamethasone</b>", "0.1 mg/kg IV every 6 hrs", "Prevents airway edema; onset 4\u20136 hours"],
]
elems.append(multi_col(adj,
[USABLE*0.20, USABLE*0.30, USABLE*0.50],
header_bg=C_BLUE))
elems.append(sp(3))
# ── NPPE Management ──────────────────────────────────
elems.append(sub_head("Management of Negative Pressure Pulmonary Edema (NPPE)",
bg=C_LTRED, fg=C_RED))
elems.append(sp(1))
elems.append(info_box(
"<i>Occurs due to extreme negative intrathoracic pressure generated by forceful "
"inspiratory effort against a closed glottis \u2192 pulmonary capillary fluid transudation \u2192 pulmonary edema.</i>",
bg=C_LTRED, fg=C_RED))
elems.append(sp(1))
nppe = ["100% oxygen",
"CPAP / PEEP ventilation",
"Mechanical ventilation if required",
"Head-end elevation (30\u00b0)",
"Diuretics: Furosemide IV"]
nppe_data = [[Paragraph(f"\u2022 {nppe[i]}", B1),
Paragraph(f"\u2022 {nppe[i+1]}" if i+1 < len(nppe) else "", B1)]
for i in range(0, len(nppe), 2)]
nt = Table(nppe_data, colWidths=[USABLE*0.5, USABLE*0.5])
nt.setStyle(TableStyle([
("TOPPADDING", (0,0), (-1,-1), 2),
("BOTTOMPADDING", (0,0), (-1,-1), 2),
("LEFTPADDING", (0,0), (-1,-1), 4),
("RIGHTPADDING", (0,0), (-1,-1), 4),
("VALIGN", (0,0), (-1,-1), "TOP"),
]))
elems.append(nt)
elems.append(sp(3))
# ── Prevention ───────────────────────────────────────
elems.append(sec_head("PREVENTION OF POST-EXTUBATION LARYNGOSPASM", bg=C_NAVY))
elems.append(sp(2))
prev = [
"Adequate suctioning (oropharyngeal + nasopharyngeal) before extubation",
"Extubate in <b>deep anesthesia</b> OR when <b>fully awake</b> — avoid the intermediate plane",
"Lateral recovery (tonsil) position",
"<b>IV Lidocaine 1\u20131.5 mg/kg</b> given 2\u20133 minutes before extubation",
"Topical lidocaine applied to the larynx",
"<b>IV Magnesium sulphate</b> — reduces neuromuscular excitability",
"Gentle, careful airway manipulation throughout",
]
prev_data = [[Paragraph(f"\u2022 {prev[i]}", B1),
Paragraph(f"\u2022 {prev[i+1]}" if i+1 < len(prev) else "", B1)]
for i in range(0, len(prev), 2)]
pt = Table(prev_data, colWidths=[USABLE*0.5, USABLE*0.5])
pt.setStyle(TableStyle([
("TOPPADDING", (0,0), (-1,-1), 2),
("BOTTOMPADDING", (0,0), (-1,-1), 2),
("LEFTPADDING", (0,0), (-1,-1), 4),
("RIGHTPADDING", (0,0), (-1,-1), 4),
("VALIGN", (0,0), (-1,-1), "TOP"),
]))
elems.append(pt)
elems.append(sp(3))
# ── Differential Diagnosis ───────────────────────────
elems.append(sub_head("Differential Diagnosis of Post-Extubation Stridor", bg=C_LTBLUE, fg=C_NAVY))
elems.append(sp(1))
dd = ["Laryngomalacia / Tracheomalacia", "Vocal cord palsy",
"Laryngeal edema", "Airway hematoma",
"Soft tissue obstruction", "Retained throat pack / Foreign body",
"Bronchospasm", "Tracheal obstruction"]
dd_data = [[Paragraph(f"\u2022 {dd[i]}", B1),
Paragraph(f"\u2022 {dd[i+1]}" if i+1 < len(dd) else "", B1)]
for i in range(0, len(dd), 2)]
dt = Table(dd_data, colWidths=[USABLE*0.5, USABLE*0.5])
dt.setStyle(TableStyle([
("TOPPADDING", (0,0), (-1,-1), 2),
("BOTTOMPADDING", (0,0), (-1,-1), 2),
("LEFTPADDING", (0,0), (-1,-1), 4),
("RIGHTPADDING", (0,0), (-1,-1), 4),
("VALIGN", (0,0), (-1,-1), "TOP"),
]))
elems.append(dt)
elems.append(sp(3))
# ── Viva Points ──────────────────────────────────────
elems.append(sec_head("VIVA POINTS — LARYNGOSPASM", bg=C_TEAL))
elems.append(sp(2))
elems.append(viva_table([
("Most common cause:", "Light plane of anesthesia with airway stimulation"),
("Most common surgeries:", "ENT surgeries (tonsillectomy, adenoidectomy), especially in children"),
("Drug of choice:", "Succinylcholine"),
("Succinylcholine IV (spasm):", "0.1\u20130.5 mg/kg IV — breaks spasm while preserving breathing"),
("Succinylcholine IV (intub.):", "1\u20132 mg/kg IV — for rapid paralysis + intubation"),
("Succinylcholine IM:", "4\u20135 mg/kg (when no IV access)"),
("Propofol dose:", "1\u20132 mg/kg IV — to deepen anesthesia"),
("Larson's Maneuver:", "Firm bilateral pressure posterior to angle of mandible, anterior to mastoid process"),
("CPAP:", "20\u201340 cm H\u2082O with 100% oxygen"),
("Least irritant volatile:", "Sevoflurane"),
("Most irritant volatile:", "Ether"),
("Major complication:", "Negative Pressure Pulmonary Edema (NPPE)"),
("Prevention key points:", "Adequate suctioning \u2022 Awake/deep extubation \u2022 IV/topical lidocaine \u2022 MgSO\u2084"),
]))
elems.append(PageBreak())
return elems
# ══════════════════════════════════════════════════════════════════
# PART II — INTRAOPERATIVE BRONCHOSPASM
# ══════════════════════════════════════════════════════════════════
def part2_bronchospasm():
elems = []
# Chapter banner
ch = Table([[Paragraph("PART II", ps("CH2S", fontName="Helvetica",
fontSize=11, textColor=HexColor("#90CAF9"), leading=14)),
Paragraph("INTRAOPERATIVE BRONCHOSPASM", ps("CH2T", fontName="Helvetica-Bold",
fontSize=18, textColor=C_WHITE, leading=22))]],
colWidths=[22*mm, USABLE - 22*mm])
ch.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), C_NAVY),
("TOPPADDING", (0,0), (-1,-1), 10),
("BOTTOMPADDING", (0,0), (-1,-1), 10),
("LEFTPADDING", (0,0), (-1,-1), 10),
("RIGHTPADDING", (0,0), (-1,-1), 10),
("VALIGN", (0,0), (-1,-1), "MIDDLE"),
("LINEBEFORE", (1,0), (1,-1), 2, C_BLUE),
]))
elems.append(ch)
elems.append(sp(4))
# ── Definition ───────────────────────────────────────
elems.append(sec_head("DEFINITION", bg=C_NAVY))
elems.append(sp(2))
elems.append(info_box(
"Bronchospasm is the <b>sudden contraction of bronchial smooth muscle</b> causing "
"<b>narrowing of the lower airways</b>, increased airway resistance, and airflow "
"obstruction occurring during the perioperative period.",
bg=C_LTBLUE, fg=C_NAVY))
elems.append(sp(4))
# ── Etiology ─────────────────────────────────────────
elems.append(sec_head("ETIOLOGY", bg=C_NAVY))
elems.append(sp(2))
# A+B side-by-side
col_a = [
("A. RESPIRATORY CAUSES", "", True),
("Upper Airway", "Laryngeal edema, pharyngeal tumors, tracheomalacia, foreign body", False),
("Lower Airway", "Bronchial asthma, COPD, bronchitis, bronchiectasis, cystic fibrosis", False),
("Pulmonary", "Pneumonia, pulmonary edema, respiratory infections", False),
]
col_b = [
("B. MECHANICAL CAUSES", "", True),
("Airway manipulation", "ETT insertion, endobronchial intubation", False),
("Secretions / Mucus plug", "", False),
("Aspiration", "Gastric contents", False),
]
def etio_mini(items, bg):
rows = []
for k, v, is_hdr in items:
if is_hdr:
rows.append([Paragraph(f"<b>{k}</b>",
ps("EH", fontName="Helvetica-Bold", fontSize=9.5,
textColor=C_NAVY, leading=13)),
Paragraph("", TC)])
else:
rows.append([Paragraph(f"\u2022 {k}", B1),
Paragraph(v, ps("EV", fontName="Helvetica-Oblique", fontSize=9,
textColor=C_DGRAY, leading=12))])
half = USABLE/2 - 3*mm
t = Table(rows, colWidths=[half*0.55, half*0.45])
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), 5),
("RIGHTPADDING", (0,0), (-1,-1), 5),
("BOX", (0,0), (-1,-1), 0.6, C_BORDER),
("LINEBELOW", (0,1), (-1,-1), 0.3, C_BORDER),
("VALIGN", (0,0), (-1,-1), "TOP"),
]))
return t
ab = Table([[etio_mini(col_a, C_LTBLUE),
etio_mini(col_b, C_GRAY)]],
colWidths=[USABLE/2 - 2*mm, USABLE/2 - 2*mm])
ab.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), 3),
("VALIGN", (0,0), (-1,-1), "TOP"),
]))
elems.append(ab)
elems.append(sp(2))
# C. Drug-induced
elems.append(sub_head("C. Drug-Induced Causes", bg=C_LTBLUE, fg=C_NAVY))
elems.append(sp(1))
drug_rows = [
["Drug/Class", "Examples"],
["Volatile agents", "Desflurane (esp. smokers), Isoflurane (induction)"],
["Opioids", "Morphine (histamine release)"],
["Muscle relaxants", "Atracurium, Mivacurium, Rapacuronium, Doxacurium"],
["Cardiovascular", "Beta-blockers"],
["Analgesics", "Aspirin, NSAIDs"],
["Reversal agents", "Neostigmine"],
["Anticholinergics (high dose)", "Atropine (>1 mg), Glycopyrrolate (>0.5 mg)"],
]
elems.append(two_col(drug_rows[1:], hdr=drug_rows[0], w1=0.35, alt_bg=C_GRAY))
elems.append(sp(2))
# D+E side-by-side
d_items = [("D. ALLERGY / ANAPHYLAXIS", "", True),
("Latex allergy", "", False), ("Antibiotics", "", False),
("IV contrast media", "", False), ("Blood transfusion", "", False),
("Muscle relaxants", "", False)]
e_items = [("E. OTHER CAUSES", "", True),
("Light plane of anesthesia", "", False),
("Distended urinary bladder", "", False),
("Peritoneal traction", "", False),
("Carcinoid syndrome", "", False),
("Smoke inhalation", "", False),
("Vancomycin / Protamine", "", False)]
def etio_mini2(items, bg):
rows = []
for k, v, is_hdr in items:
if is_hdr:
rows.append([Paragraph(f"<b>{k}</b>",
ps("EH2", fontName="Helvetica-Bold", fontSize=9.5,
textColor=C_NAVY, leading=13))])
else:
rows.append([Paragraph(f"\u2022 {k}", B1)])
half = USABLE/2 - 3*mm
t = Table(rows, colWidths=[half])
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), 5),
("RIGHTPADDING", (0,0), (-1,-1), 5),
("BOX", (0,0), (-1,-1), 0.6, C_BORDER),
("LINEBELOW", (0,1), (-1,-1), 0.3, C_BORDER),
("VALIGN", (0,0), (-1,-1), "TOP"),
]))
return t
de = Table([[etio_mini2(d_items, C_LTRED),
etio_mini2(e_items, C_LTORANGE)]],
colWidths=[USABLE/2 - 2*mm, USABLE/2 - 2*mm])
de.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), 3),
("VALIGN", (0,0), (-1,-1), "TOP"),
]))
elems.append(de)
elems.append(sp(3))
# Stage-wise causes
elems.append(sub_head("Causes by Anesthetic Stage", bg=C_LTBLUE, fg=C_NAVY))
elems.append(sp(1))
stage = [
["Stage", "Common Causes"],
["<b>Induction</b>",
"Airway irritation, Intubation, Aspiration, Anaphylaxis, Pulmonary edema, Displaced ETT"],
["<b>Maintenance</b> (most common)",
"<b>Anaphylaxis</b>, Aspiration, Airway irritation, Endobronchial intubation, "
"Pneumothorax, Pulmonary edema, Vancomycin, Protamine"],
["<b>Emergence</b>",
"Airway irritation, Extubation, Aspiration, Pulmonary edema, Anaphylaxis, Accidental extubation"],
]
elems.append(two_col(stage[1:], hdr=stage[0], w1=0.28, alt_bg=C_GRAY))
elems.append(sp(4))
# ── Pathophysiology ──────────────────────────────────
elems.append(sec_head("PATHOPHYSIOLOGY", bg=C_NAVY))
elems.append(sp(3))
elems.append(pathway([
"Trigger (intubation / aspiration / anaphylaxis / drug / light anesthesia)",
"Bronchial Smooth Muscle Contraction",
"Bronchoconstriction \u2192 Increased Airway Resistance",
"Air Trapping \u2192 Dynamic Hyperinflation",
"V/Q Mismatch",
"Hypoxemia + Hypercapnia",
"Increased Work of Breathing \u2192 Respiratory Failure",
]))
elems.append(sp(4))
# ── Clinical Features ────────────────────────────────
elems.append(sec_head("CLINICAL FEATURES", bg=C_NAVY))
elems.append(sp(2))
# Two-column layout: clinical signs | ventilator findings
signs_items = [
"Expiratory wheeze and rhonchi",
"Prolonged expiratory phase",
"Cough",
"Reduced chest expansion",
"Reduced breath sounds",
"<b>Silent chest = severe life-threatening obstruction</b>",
]
vent_items = [
"Increased peak airway pressure",
"Decreased tidal volume",
"Difficult manual ventilation",
"Poor reservoir bag compliance",
"Falling SpO\u2082",
"Initially rising ETCO\u2082; later falling in complete obstruction",
]
def cf_mini(title, items, bg, fg):
rows = [[Paragraph(f"<b>{title}</b>",
ps("CFH", fontName="Helvetica-Bold", fontSize=9.5, textColor=fg, leading=13))]]
for it in items:
rows.append([Paragraph(f"\u2022 {it}", B1)])
half = USABLE/2 - 3*mm
t = Table(rows, colWidths=[half])
t.setStyle(TableStyle([
("BACKGROUND", (0,0), (0,0), 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),
("BOX", (0,0), (-1,-1), 0.6, fg),
("LINEBELOW", (0,0), (-1,0), 1.2, fg),
("VALIGN", (0,0), (-1,-1), "TOP"),
]))
return t
cf2 = Table([[cf_mini("Clinical Signs", signs_items, C_LTBLUE, C_NAVY),
cf_mini("Ventilator / Monitor Findings", vent_items, C_LTORANGE, C_ORANGE)]],
colWidths=[USABLE/2 - 2*mm, USABLE/2 - 2*mm])
cf2.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), 3),
("VALIGN", (0,0), (-1,-1), "TOP"),
]))
elems.append(cf2)
elems.append(sp(3))
# ── Differential Diagnosis ───────────────────────────
elems.append(sub_head("Differential Diagnosis", bg=C_LTBLUE, fg=C_NAVY))
elems.append(sp(1))
dd2 = [
["", "Differential Diagnosis"],
["Unilateral wheeze", "Endobronchial intubation \u2022 Foreign body \u2022 Tension pneumothorax \u2022 Kinked / obstructed ETT \u2022 Localized pulmonary edema"],
["Raised peak airway pressure", "Bronchospasm \u2022 Kinked tube \u2022 Secretions \u2022 Excessive TV \u2022 High flow \u2022 Pneumothorax \u2022 Pleural effusion \u2022 Trendelenburg \u2022 Ascites \u2022 Pneumoperitoneum \u2022 Abdominal packing"],
]
elems.append(two_col(dd2[1:], hdr=dd2[0], w1=0.28, alt_bg=C_GRAY))
elems.append(sp(4))
# ── Management ───────────────────────────────────────
elems.append(sec_head("MANAGEMENT", bg=C_NAVY))
elems.append(sp(2))
# Step 1
elems.append(KeepTogether([
step_head("1", "CALL FOR HELP \u2014 STOP SURGERY"),
sp(1),
b1("Inform surgical team \u2022 Switch to <b>100% FiO\u2082</b> \u2022 Manual ventilation"),
sp(2),
]))
# Step 2 — DOPE
elems.append(KeepTogether([
step_head("2", "CHECK MECHANICAL CAUSES — DOPE"),
sp(1),
alert("Always exclude mechanical causes BEFORE diagnosing bronchospasm!"),
sp(1),
dope_box(),
sp(1),
b1("Suction ETT if secretions or mucus plug suspected"),
sp(2),
]))
# Step 3
elems.append(KeepTogether([
step_head("3", "DEEPEN ANESTHESIA"),
sp(1),
]))
deep = [
["Agent", "Notes"],
["Sevoflurane", "Preferred volatile — bronchodilator, least irritant"],
["Isoflurane", "Bronchodilator — reasonable alternative"],
["Propofol", "IV agent — useful for deepening"],
["<b>Ketamine</b>", "<b>Drug of choice in asthmatics</b> — bronchodilator, sympathomimetic, releases catecholamines"],
]
elems.append(multi_col(deep, [USABLE*0.20, USABLE*0.80], header_bg=C_BLUE))
elems.append(sp(3))
# ── Pharmacological Management ───────────────────────
elems.append(sec_head("PHARMACOLOGICAL MANAGEMENT", bg=C_NAVY))
elems.append(sp(2))
# 1. Beta-2 agonists
elems.append(sub_head("1. Beta-2 Agonists — First-Line Therapy", bg=C_LTGREEN, fg=C_GREEN))
elems.append(sp(1))
b2ag = [
["Drug", "Route", "Dose", "Frequency"],
["<b>Salbutamol</b>", "Nebulization", "2.5 mg", "Every 20 min \u00d73, then 2\u20134 hourly"],
["<b>Salbutamol</b>", "MDI via ETT adapter", "4\u20138 puffs", "Alternative inhalation route"],
["<b>Terbutaline</b>", "SC", "0.25 mg", "Every 20 min, max 3 doses"],
["Albuterol / Levalbuterol", "Inhaled", "Per preparation", "Alternative agents"],
]
elems.append(multi_col(b2ag,
[USABLE*0.25, USABLE*0.20, USABLE*0.18, USABLE*0.37],
header_bg=C_GREEN))
elems.append(sp(2))
# 2. Anticholinergic
elems.append(sub_head("2. Anticholinergic", bg=C_LTBLUE, fg=C_NAVY))
elems.append(sp(1))
elems.append(b1("<b>Ipratropium bromide</b> \u2014 0.5 mg nebulization every 30 min \u00d73, then every 2\u20134 hours"))
elems.append(sp(2))
# 3. Corticosteroids
elems.append(sub_head("3. Corticosteroids", bg=C_LTBLUE, fg=C_NAVY))
elems.append(sp(1))
cs = [
["Drug", "Dose"],
["Methylprednisolone", "125 mg IV bolus, then 40\u201360 mg IV every 6 hrs"],
["Hydrocortisone", "200 mg IV every 4\u20136 hrs"],
["Dexamethasone", "IV as adjunct"],
]
elems.append(multi_col(cs, [USABLE*0.30, USABLE*0.70], header_bg=C_BLUE))
elems.append(sp(1))
elems.append(p("<i>Note: Corticosteroids have delayed onset (4\u20136 hours). They do NOT provide immediate bronchodilation.</i>", NOTE))
elems.append(sp(2))
# 4-6 side by side
def drug_mini(title, rows, bg, fg):
trows = [Paragraph(f"<b>{title}</b>",
ps("DM", fontName="Helvetica-Bold", fontSize=9.5, textColor=fg, leading=13))]
t_inner = [trows]
for r in rows:
t_inner.append([Paragraph(r, B1)])
half = USABLE/3 - 3*mm
t = Table(t_inner, colWidths=[half])
t.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,0), bg),
("TOPPADDING", (0,0), (-1,-1), 3),
("BOTTOMPADDING", (0,0), (-1,-1), 3),
("LEFTPADDING", (0,0), (-1,-1), 5),
("RIGHTPADDING", (0,0), (-1,-1), 5),
("BOX", (0,0), (-1,-1), 0.6, fg),
("LINEBELOW", (0,0), (-1,0), 1, fg),
("VALIGN", (0,0), (-1,-1), "TOP"),
]))
return t
trio = Table([[
drug_mini("4. Epinephrine",
["<b>Indications:</b> severe bronchospasm, anaphylaxis",
"0.3 mL of 1:1000 IM",
"IV: 0.5\u20132 mcg/min infusion"],
C_LTRED, C_RED),
drug_mini("5. Magnesium Sulphate",
["<b>1\u20132 g IV over 20 min</b>",
"Refractory bronchospasm",
"Smooth muscle relaxation"],
C_LTPURPLE, C_PURPLE),
drug_mini("6. Aminophylline",
["Loading: <b>5 mg/kg IV slowly</b>",
"Maintenance: 0.5\u20130.9 mg/kg/hr",
"Adjust to serum levels"],
C_LTORANGE, C_ORANGE),
]], colWidths=[USABLE/3 - 2*mm, USABLE/3 - 2*mm, USABLE/3 - 2*mm])
trio.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), 3),
("VALIGN", (0,0), (-1,-1), "TOP"),
]))
elems.append(trio)
elems.append(sp(2))
# 7. Heliox
elems.append(sub_head("7. Heliox", bg=C_LTBLUE, fg=C_NAVY))
elems.append(sp(1))
elems.append(b1("Helium 70\u201379% + Oxygen 21\u201330%"))
elems.append(b1("Low-density gas \u2192 reduces turbulent airflow \u2192 decreases airway resistance"))
elems.append(b1("Improves ventilation distribution and reduces work of breathing"))
elems.append(sp(3))
# ── Ventilator Strategy ──────────────────────────────
elems.append(sec_head("VENTILATOR STRATEGY", bg=C_NAVY))
elems.append(sp(2))
vs = [
["Parameter", "Target / Rationale"],
["Respiratory rate", "Low (8\u201312/min)"],
["Tidal volume", "6\u20138 mL/kg"],
["Inspiratory flow", "High \u2192 leaves more time for expiration"],
["I:E ratio", "<b>1:3 to 1:5</b> \u2014 prolonged expiratory time"],
["Auto-PEEP", "Avoid \u2014 monitor end-expiratory flow; allow breath-stacking time"],
["Permissive hypercapnia", "Accept if necessary to prevent barotrauma"],
]
elems.append(two_col(vs[1:], hdr=vs[0], w1=0.35, alt_bg=C_GRAY))
elems.append(sp(3))
# ── Prevention ───────────────────────────────────────
elems.append(sec_head("PREVENTION", bg=C_NAVY))
elems.append(sp(2))
prev2 = [
"Optimize asthma / COPD well before elective surgery",
"Continue preoperative bronchodilators on the day of surgery",
"Administer steroids in high-risk patients (start 24\u201348 h preoperatively)",
"Prefer <b>LMA over ETT</b> when clinically appropriate",
"Maintain adequate depth of anesthesia throughout",
"Gentle airway manipulation and careful suctioning",
"Adequate multimodal analgesia",
"Deep extubation in selected patients",
"Incentive spirometry and early mobilization postoperatively",
]
prev2_data = [[Paragraph(f"\u2022 {prev2[i]}", B1),
Paragraph(f"\u2022 {prev2[i+1]}" if i+1 < len(prev2) else "", B1)]
for i in range(0, len(prev2), 2)]
pt2 = Table(prev2_data, colWidths=[USABLE*0.5, USABLE*0.5])
pt2.setStyle(TableStyle([
("TOPPADDING", (0,0), (-1,-1), 2),
("BOTTOMPADDING", (0,0), (-1,-1), 2),
("LEFTPADDING", (0,0), (-1,-1), 4),
("RIGHTPADDING", (0,0), (-1,-1), 4),
("VALIGN", (0,0), (-1,-1), "TOP"),
]))
elems.append(pt2)
elems.append(sp(3))
# ── Complications ────────────────────────────────────
elems.append(sub_head("Complications", bg=C_LTRED, fg=C_RED))
elems.append(sp(1))
comp = ["Hypoxemia and hypercapnia",
"Dynamic hyperinflation and auto-PEEP",
"Barotrauma and pneumothorax",
"Hypotension",
"Cardiac arrest",
"Respiratory failure / ICU admission"]
comp_d = [[Paragraph(f"\u2022 {comp[i]}", B1),
Paragraph(f"\u2022 {comp[i+1]}" if i+1 < len(comp) else "", B1)]
for i in range(0, len(comp), 2)]
ct2 = Table(comp_d, colWidths=[USABLE*0.5, USABLE*0.5])
ct2.setStyle(TableStyle([
("TOPPADDING", (0,0), (-1,-1), 2),
("BOTTOMPADDING", (0,0), (-1,-1), 2),
("LEFTPADDING", (0,0), (-1,-1), 4),
("RIGHTPADDING", (0,0), (-1,-1), 4),
("VALIGN", (0,0), (-1,-1), "TOP"),
]))
elems.append(ct2)
elems.append(sp(3))
# ── Viva Points ──────────────────────────────────────
elems.append(sec_head("VIVA POINTS — BRONCHOSPASM", bg=C_TEAL))
elems.append(sp(2))
elems.append(viva_table([
("Most common cause (intraop):", "Airway irritation in light plane of anesthesia"),
("Most common cause (maint.):", "Anaphylaxis"),
("DOC in asthmatics:", "Ketamine (bronchodilator + sympathomimetic)"),
("First-line bronchodilator:", "Salbutamol (beta-2 agonist nebulization)"),
("Steroid onset:", "4\u20136 hours — no immediate bronchodilation"),
("Magnesium dose:", "1\u20132 g IV over 20 min for refractory bronchospasm"),
("DOPE mnemonic:", "Displacement \u2022 Obstruction \u2022 Pneumothorax \u2022 Equipment failure"),
("Silent chest means:", "Severe life-threatening bronchospasm — immediate action required"),
("I:E ratio target:", "1:3 to 1:5 (prolonged expiration, prevents air trapping)"),
("Permissive hypercapnia:", "Accepted strategy to avoid barotrauma in severe obstruction"),
("Avoid in bronchospasm:", "Desflurane, Morphine, Beta-blockers, Neostigmine, Atracurium"),
("Desflurane:", "Most irritant volatile agent — avoid in reactive airway disease"),
]))
elems.append(PageBreak())
return elems
# ══════════════════════════════════════════════════════════════════
# PART III — COMPARISON TABLE
# ══════════════════════════════════════════════════════════════════
def part3_comparison():
elems = []
ch = Table([[Paragraph("PART III", ps("CH3S", fontName="Helvetica",
fontSize=11, textColor=HexColor("#90CAF9"), leading=14)),
Paragraph("COMPARISON TABLE", ps("CH3T", fontName="Helvetica-Bold",
fontSize=18, textColor=C_WHITE, leading=22))]],
colWidths=[22*mm, USABLE - 22*mm])
ch.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), C_NAVY),
("TOPPADDING", (0,0), (-1,-1), 10),
("BOTTOMPADDING", (0,0), (-1,-1), 10),
("LEFTPADDING", (0,0), (-1,-1), 10),
("RIGHTPADDING", (0,0), (-1,-1), 10),
("VALIGN", (0,0), (-1,-1), "MIDDLE"),
("LINEBEFORE", (1,0), (1,-1), 2, C_BLUE),
]))
elems.append(ch)
elems.append(sp(4))
comp_rows = [
["Feature", "Laryngospasm", "Bronchospasm"],
["Site", "Upper airway\n(glottis / vocal cords)", "Lower airway\n(bronchial smooth muscle)"],
["Mechanism", "Reflex adductor muscle contraction\n(neurogenic)", "Smooth muscle contraction\n(inflammatory / reflex)"],
["Typical sound", "Inspiratory stridor\nSilent chest (complete)", "Expiratory wheeze\nSilent chest (severe)"],
["SpO\u2082 pattern", "Rapid, severe desaturation", "Gradual desaturation"],
["Peak airway pressure", "Less markedly elevated", "Markedly elevated"],
["ETCO\u2082", "Rises then falls rapidly", "Initially rises; falls in severe obstruction"],
["Drug of choice", "Succinylcholine", "Salbutamol\n(Ketamine in asthmatics)"],
["Deepen with", "Propofol 1\u20132 mg/kg IV", "Ketamine / Sevoflurane"],
["Key technique", "Larson\u2019s Maneuver", "DOPE check first"],
["CPAP / ventilation", "CPAP 20\u201340 cm H\u2082O", "I:E 1:3\u20131:5;\nAvoid auto-PEEP"],
["Preferred volatile", "Sevoflurane (least irritant)", "Sevoflurane / Isoflurane"],
["Most irritant volatile", "Ether", "Desflurane"],
["Main complication", "Negative Pressure\nPulmonary Edema (NPPE)", "Auto-PEEP,\nBarotrauma, Pneumothorax"],
["Drugs to avoid", "Ether, morphine,\nbarbiturates, desflurane", "Desflurane, morphine,\nbeta-blockers, neostigmine"],
["Reversal / antidote", "Succinylcholine (muscle relaxant)", "Salbutamol + steroids + MgSO\u2084"],
]
c_data = []
for i, row in enumerate(comp_rows):
cell_ps = lambda txt, bold=False, color=C_BLACK: Paragraph(txt,
ps(f"CMP{i}", fontName="Helvetica-Bold" if bold else "Helvetica",
fontSize=9.5, textColor=color, leading=13))
if i == 0:
c_data.append([Paragraph(f"<b>{c}</b>", TH) for c in row])
else:
c_data.append([
Paragraph(f"<b>{row[0]}</b>", ps(f"CK{i}", fontName="Helvetica-Bold",
fontSize=9.5, textColor=C_NAVY, leading=13)),
Paragraph(row[1], ps(f"CA{i}", fontName="Helvetica", fontSize=9.5,
textColor=C_BLACK, leading=13)),
Paragraph(row[2], ps(f"CB{i}", fontName="Helvetica", fontSize=9.5,
textColor=C_BLACK, leading=13)),
])
c1, c2, c3 = USABLE*0.24, USABLE*0.38, USABLE*0.38
comp_t = Table(c_data, colWidths=[c1, c2, c3])
comp_style = [
("BACKGROUND", (0,0), (-1,0), C_NAVY),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING", (0,0), (-1,-1), 5),
("LEFTPADDING", (0,0), (-1,-1), 6),
("RIGHTPADDING", (0,0), (-1,-1), 6),
("BOX", (0,0), (-1,-1), 0.8, C_NAVY),
("LINEBELOW", (0,0), (-1,-1), 0.4, C_BORDER),
("VALIGN", (0,0), (-1,-1), "TOP"),
]
for i in range(1, len(c_data), 2):
comp_style.append(("BACKGROUND", (0,i), (-1,i), C_LTBLUE))
comp_t.setStyle(TableStyle(comp_style))
elems.append(comp_t)
elems.append(sp(5))
# Final note
elems.append(hr(C_NAVY, 1))
elems.append(sp(2))
elems.append(p(
"These notes are prepared for examination preparation. Always follow current "
"institutional protocols and published clinical guidelines for patient management.",
ps("FN", fontName="Helvetica-Oblique", fontSize=8.5, textColor=C_DGRAY,
leading=12, alignment=TA_CENTER)
))
return elems
# ══════════════════════════════════════════════════════════════════
# ASSEMBLE
# ══════════════════════════════════════════════════════════════════
story += cover_page()
story += part1_laryngospasm()
story += part2_bronchospasm()
story += part3_comparison()
doc.build(story)
print("PDF generated: Airway_Emergencies_EXAM.pdf")
cd /home/daytona/workspace/anesthesia-notes && python exam_notes.py 2>&1
ls -lh /home/daytona/workspace/anesthesia-notes/Airway_Emergencies_EXAM.pdf
Airway Emergencies EXAM
PDF Document · PDF