mkdir -p /tmp/workspace/life-threatening-rashes && cat > /tmp/workspace/life-threatening-rashes/generate_pdf.py << 'PYEOF'
from reportlab.lib.pagesizes import A4
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.units import cm
from reportlab.lib import colors
from reportlab.platypus import (SimpleDocTemplate, Paragraph, Spacer, Table,
TableStyle, HRFlowable, KeepTogether)
from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_JUSTIFY
from reportlab.platypus import PageBreak
output_path = "/tmp/workspace/life-threatening-rashes/Life_Threatening_Rashes_MD_Emergency_Medicine.pdf"
doc = SimpleDocTemplate(
output_path,
pagesize=A4,
rightMargin=1.8*cm,
leftMargin=1.8*cm,
topMargin=2*cm,
bottomMargin=2*cm,
title="Life-Threatening Rashes - MD Emergency Medicine",
author="Orris Medical AI"
)
styles = getSampleStyleSheet()
# Custom styles
title_style = ParagraphStyle(
'CustomTitle',
parent=styles['Title'],
fontSize=20,
textColor=colors.HexColor('#1a3a5c'),
spaceAfter=6,
alignment=TA_CENTER,
fontName='Helvetica-Bold'
)
subtitle_style = ParagraphStyle(
'Subtitle',
parent=styles['Normal'],
fontSize=11,
textColor=colors.HexColor('#4a6fa5'),
spaceAfter=14,
alignment=TA_CENTER,
fontName='Helvetica'
)
h1_style = ParagraphStyle(
'H1',
parent=styles['Heading1'],
fontSize=14,
textColor=colors.white,
backColor=colors.HexColor('#1a3a5c'),
spaceBefore=14,
spaceAfter=6,
leftIndent=-8,
rightIndent=-8,
borderPadding=(5, 8, 5, 8),
fontName='Helvetica-Bold'
)
h2_style = ParagraphStyle(
'H2',
parent=styles['Heading2'],
fontSize=12,
textColor=colors.white,
backColor=colors.HexColor('#2e6da4'),
spaceBefore=10,
spaceAfter=4,
leftIndent=-4,
rightIndent=-4,
borderPadding=(4, 6, 4, 6),
fontName='Helvetica-Bold'
)
h3_style = ParagraphStyle(
'H3',
parent=styles['Heading3'],
fontSize=11,
textColor=colors.HexColor('#1a3a5c'),
spaceBefore=8,
spaceAfter=3,
fontName='Helvetica-Bold',
borderPadding=(2, 0, 2, 0),
)
body_style = ParagraphStyle(
'Body',
parent=styles['Normal'],
fontSize=9.5,
textColor=colors.HexColor('#222222'),
spaceAfter=4,
leading=14,
fontName='Helvetica'
)
bullet_style = ParagraphStyle(
'Bullet',
parent=styles['Normal'],
fontSize=9.5,
textColor=colors.HexColor('#222222'),
spaceAfter=3,
leading=13,
leftIndent=14,
bulletIndent=4,
fontName='Helvetica'
)
sub_bullet_style = ParagraphStyle(
'SubBullet',
parent=styles['Normal'],
fontSize=9,
textColor=colors.HexColor('#333333'),
spaceAfter=2,
leading=12,
leftIndent=28,
bulletIndent=18,
fontName='Helvetica'
)
highlight_style = ParagraphStyle(
'Highlight',
parent=styles['Normal'],
fontSize=9.5,
textColor=colors.HexColor('#7b0000'),
spaceAfter=3,
leading=13,
leftIndent=10,
fontName='Helvetica-Bold'
)
warning_style = ParagraphStyle(
'Warning',
parent=styles['Normal'],
fontSize=9.5,
backColor=colors.HexColor('#fff3cd'),
textColor=colors.HexColor('#856404'),
spaceAfter=4,
leading=13,
leftIndent=8,
rightIndent=8,
borderPadding=(4, 6, 4, 6),
fontName='Helvetica-Bold'
)
tip_style = ParagraphStyle(
'Tip',
parent=styles['Normal'],
fontSize=9.5,
backColor=colors.HexColor('#d4edda'),
textColor=colors.HexColor('#155724'),
spaceAfter=4,
leading=13,
leftIndent=8,
rightIndent=8,
borderPadding=(4, 6, 4, 6),
fontName='Helvetica'
)
caption_style = ParagraphStyle(
'Caption',
parent=styles['Normal'],
fontSize=8,
textColor=colors.HexColor('#555555'),
spaceAfter=4,
leading=11,
alignment=TA_CENTER,
fontName='Helvetica-Oblique'
)
# Table header/cell styles
def make_table(data, col_widths=None, header_bg=colors.HexColor('#1a3a5c'), alt_bg=colors.HexColor('#eaf0fb')):
t = Table(data, colWidths=col_widths, repeatRows=1)
style = TableStyle([
('BACKGROUND', (0,0), (-1,0), header_bg),
('TEXTCOLOR', (0,0), (-1,0), colors.white),
('FONTNAME', (0,0), (-1,0), 'Helvetica-Bold'),
('FONTSIZE', (0,0), (-1,0), 8.5),
('ALIGN', (0,0), (-1,-1), 'LEFT'),
('VALIGN', (0,0), (-1,-1), 'TOP'),
('FONTNAME', (0,1), (-1,-1), 'Helvetica'),
('FONTSIZE', (0,1), (-1,-1), 8),
('ROWBACKGROUNDS', (0,1), (-1,-1), [colors.white, alt_bg]),
('GRID', (0,0), (-1,-1), 0.4, colors.HexColor('#b0b8c8')),
('TOPPADDING', (0,0), (-1,-1), 4),
('BOTTOMPADDING', (0,0), (-1,-1), 4),
('LEFTPADDING', (0,0), (-1,-1), 5),
('RIGHTPADDING', (0,0), (-1,-1), 5),
('WORDWRAP', (0,0), (-1,-1), True),
])
t.setStyle(style)
return t
def B(text):
return f'<b>{text}</b>'
def I(text):
return f'<i>{text}</i>'
def R(text):
return f'<font color="#c0392b"><b>{text}</b></font>'
story = []
W = doc.width
# ─── COVER ─────────────────────────────────────────────────────────────────
story.append(Spacer(1, 1.5*cm))
story.append(Paragraph("LIFE-THREATENING RASHES", title_style))
story.append(Paragraph("MD Emergency Medicine Exam Guide", subtitle_style))
story.append(HRFlowable(width="100%", thickness=2, color=colors.HexColor('#1a3a5c'), spaceAfter=10))
story.append(Paragraph(
"TEN · SSSS · STSS · DRESS · Pemphigus Vulgaris · Necrotizing Fasciitis · Lynch Rash Algorithm",
ParagraphStyle('Topics', parent=styles['Normal'], fontSize=10.5, textColor=colors.HexColor('#2e6da4'),
alignment=TA_CENTER, spaceAfter=6, fontName='Helvetica-Bold')
))
story.append(HRFlowable(width="100%", thickness=1, color=colors.HexColor('#b0b8c8'), spaceAfter=10))
story.append(Paragraph(
"Compiled from Harrison's 22E, Tintinalli's Emergency Medicine, Dermatology 2-Volume Set 5e, Fitzpatrick's Dermatology, Goldman-Cecil Medicine, Andrews' Diseases of the Skin",
caption_style
))
story.append(Spacer(1, 0.5*cm))
# ─── LYNCH ALGORITHM ───────────────────────────────────────────────────────
story.append(Paragraph("THE MODIFIED LYNCH RASH ALGORITHM", h1_style))
story.append(Paragraph(
"The <b>Modified Lynch Algorithm</b> (Lynch & Edminster, Ann Emerg Med) is the standard framework for the emergency physician to systematically approach any rash. "
"It categorizes rashes into <b>6 morphological groups</b> and branches to life-threatening diagnoses.",
body_style
))
story.append(Spacer(1, 0.2*cm))
lynch_data = [
[B('Rash Type'), B('Key Life-Threatening Conditions')],
['Maculopapular', 'Meningococcemia, RMSF, TSS, Kawasaki, early SJS'],
['Petechial / Purpuric', 'Meningococcemia, DIC, TTP, RMSF, HSP'],
['Diffuse Erythematous', 'Staphylococcal TSS, STSS, SSSS, Kawasaki, scarlet fever'],
['Vesiculo-Bullous', 'TEN, SJS, SSSS, Pemphigus Vulgaris, NF (late)'],
['Pustular', 'Disseminated gonorrhea, bacterial folliculitis'],
['Non-erythematous', 'Secondary syphilis, anthrax (ulcerated lesions)'],
]
story.append(make_table(lynch_data, col_widths=[5.5*cm, W-5.5*cm]))
story.append(Spacer(1, 0.3*cm))
story.append(Paragraph(B("Algorithm Branch Logic:"), h3_style))
for pt in [
"1. Is the rash <b>fluid-filled or solid?</b>",
"2. If fluid-filled - is it erythematous, clear, or pustular?",
"3. Vesiculo-bullous → TEN/SJS, SSSS, Pemphigus Vulgaris, NF (late)",
"4. Diffuse erythematous (non-fluid) → TSS, STSS, SSSS (early), Kawasaki, DRESS",
]:
story.append(Paragraph(f"\u2022 {pt}", bullet_style))
story.append(Paragraph(
R("\u26a0 EXAM TIP: Lynch algorithm is morphology-based — identify the primary lesion type first, then branch to life-threatening differentials."),
warning_style
))
# ─── TEN ───────────────────────────────────────────────────────────────────
story.append(Paragraph("1. TOXIC EPIDERMAL NECROLYSIS (TEN)", h1_style))
story.append(Paragraph(B("Classification (Bastuji-Garin Criteria)"), h3_style))
ten_class = [
[B('Diagnosis'), B('BSA Detachment')],
['Stevens-Johnson Syndrome (SJS)', '< 10%'],
['SJS / TEN Overlap', '10 – 30%'],
['Toxic Epidermal Necrolysis (TEN)', '> 30%'],
]
story.append(make_table(ten_class, col_widths=[9*cm, W-9*cm]))
story.append(Spacer(1, 0.3*cm))
story.append(Paragraph(B("Pathophysiology"), h3_style))
for pt in [
"Drug-induced (>85%) T-cell mediated cytotoxic attack on keratinocytes → <b>full-thickness epidermal necrosis</b>",
"Key culprit drugs: <b>allopurinol, sulfonamides (co-trimoxazole), anticonvulsants</b> (phenytoin, carbamazepine, phenobarbital, lamotrigine), NSAIDs (oxicams), nevirapine",
"Genetic risk: HLA-B*5801 (allopurinol), HLA-B*1502 (carbamazepine in Asians)",
]:
story.append(Paragraph(f"\u2022 {pt}", bullet_style))
story.append(Paragraph(B("Clinical Features"), h3_style))
for pt in [
"<b>Prodrome (1-3 days):</b> Fever, burning/painful skin, conjunctival injection, odynophagia",
"<b>Skin:</b> Dusky erythematous macules → atypical target lesions → epidermal detachment; large flaccid bullae",
"<b>Nikolsky sign positive</b> – lateral pressure shears epidermis",
"<b>Asboe-Hansen sign positive</b> – pressure on blister causes lateral extension",
"<b>Mucosal involvement (>90%):</b> oral, ocular (pseudomembranes, symblepharon), urogenital, respiratory",
"Detachment leaves raw, weeping, erythematous dermis (resembles partial-thickness burn)",
]:
story.append(Paragraph(f"\u2022 {pt}", bullet_style))
story.append(Paragraph(B("SCORTEN Severity Score (1 point each)"), h3_style))
scorten_data = [
[B('Parameter'), B('Threshold'), B('Points')],
['Age', '> 40 years', '1'],
['Heart rate', '> 120 bpm', '1'],
['Cancer / hematologic malignancy', 'Present', '1'],
['BSA detachment', '> 10%', '1'],
['Serum urea', '> 10 mmol/L (> 28 mg/dL)', '1'],
['Serum bicarbonate', '< 20 mmol/L', '1'],
['Serum glucose', '> 14 mmol/L (> 252 mg/dL)', '1'],
]
story.append(make_table(scorten_data, col_widths=[6.5*cm, 6.5*cm, 2.5*cm]))
story.append(Paragraph(R("\u26a0 SCORTEN 0-1 = 3.2% mortality | SCORTEN ≥5 = >90% mortality"), highlight_style))
story.append(Spacer(1, 0.2*cm))
story.append(Paragraph(B("Management"), h3_style))
for pt in [
"<b>STOP the offending drug immediately</b> (most important first step)",
"Admit to burn unit / ICU; wound care with non-adherent biological dressings",
"Fluid and electrolyte replacement (manage like a burn patient); nutritional support (NGT)",
"<b>Ophthalmology consult early</b> (daily eye care, lubricants, lysis of symblepharon)",
"<b>Cyclosporine</b> 3-5 mg/kg/day – currently most evidence-supported (inhibits CD8 T-cells)",
"<b>IVIG</b> 2-3 g/kg over 3-4 days – blocks Fas-FasL mediated keratinocyte apoptosis; evidence mixed",
"<b>Systemic steroids:</b> Controversial – may increase infection risk; early short course used in some centers",
"<b>Etanercept</b> (TNF-α inhibitor) – emerging evidence where cyclosporine contraindicated",
"Antibiotics only for <i>documented</i> infection (NOT prophylactic); DVT prophylaxis",
]:
story.append(Paragraph(f"\u2022 {pt}", bullet_style))
# ─── SSSS ───────────────────────────────────────────────────────────────────
story.append(Paragraph("2. STAPHYLOCOCCAL SCALDED SKIN SYNDROME (SSSS)", h1_style))
story.append(Paragraph(B("Pathophysiology"), h3_style))
for pt in [
"Caused by <b>S. aureus phage group II (types 55 & 71)</b> – MSSA or MRSA",
"Produces <b>exfoliative toxins A and B (ETA/ETB)</b> – serine proteases that cleave <b>desmoglein-1 (Dsg1)</b> in the granular layer",
"Toxin disseminates <b>hematogenously</b> from a distant focus (nasopharynx/conjunctivae in children; pneumonia/bacteremia in adults)",
"Epidermis splits at the <b>granular layer</b> → intraepidermal (superficial) cleavage — unlike TEN (full-thickness)",
"Primarily affects children <5 yrs (low renal clearance of toxins, lack of anti-ET antibodies); adults with renal failure/immunosuppression",
]:
story.append(Paragraph(f"\u2022 {pt}", bullet_style))
story.append(Paragraph(B("Clinical Features"), h3_style))
for pt in [
"<b>Prodrome:</b> Fever, irritability, skin tenderness, purulent rhinorrhea/conjunctivitis",
"Perioral/periorbital erythema initially → generalizes to trunk within 24-48 hours",
"<b>Nikolsky sign positive</b> – but cleavage is superficial (granular layer)",
"Superficial, flaccid, <b>sterile</b> bullae → sheets of epidermis peel off, leaving moist glistening base",
"<b>NO mucosal involvement</b> (KEY – Dsg1 absent from mucosa)",
"Perioral radial crusting ('sad man face')",
]:
story.append(Paragraph(f"\u2022 {pt}", bullet_style))
story.append(Paragraph(B("SSSS vs. TEN – Key Differentiating Table"), h3_style))
diff_data = [
[B('Feature'), B('SSSS'), B('TEN')],
['Age group', 'Children <5; immunocompromised adults', 'Adults mainly (any age)'],
['Cause', 'S. aureus exfoliative toxin', 'Drug reaction (T-cell mediated)'],
['Cleavage plane', 'Intraepidermal (granular layer)', 'Full-thickness dermoepidermal junction'],
['Mucosal involvement', 'ABSENT', 'PRESENT (>90%)'],
['Nikolsky sign', 'Positive', 'Positive'],
['Skin biopsy', 'Superficial split, no necrosis', 'Full-thickness epidermal necrosis'],
['Frozen section', 'Superficial cleavage (few layers)', 'Full-thickness necrosis'],
['Mortality', '<5% in children; higher in adults', '30–50%'],
]
story.append(make_table(diff_data, col_widths=[4.5*cm, 6*cm, 5*cm]))
story.append(Spacer(1, 0.2*cm))
story.append(Paragraph(B("Management"), h3_style))
for pt in [
"<b>IV antistaphylococcal antibiotics:</b> nafcillin/oxacillin (MSSA); <b>vancomycin</b> (MRSA/unknown)",
"Gentle wound care, non-adherent dressings; fluid/electrolyte management",
"<b>Do NOT use corticosteroids</b> (worsens infection)",
"Treat underlying staphylococcal focus",
]:
story.append(Paragraph(f"\u2022 {pt}", bullet_style))
# ─── STSS ───────────────────────────────────────────────────────────────────
story.append(Paragraph("3. STREPTOCOCCAL TOXIC SHOCK SYNDROME (STSS)", h1_style))
story.append(Paragraph(B("CDC 2010 Case Definition"), h3_style))
stss_data = [
[B('Criterion'), B('Requirement')],
['Criterion IA', 'Isolation of GAS from a STERILE site (blood, CSF, pleural fluid, biopsy)'],
['Criterion IB', 'Isolation of GAS from a NON-STERILE site'],
['Criterion IIA', 'Hypotension (SBP ≤90 mmHg in adults)'],
['Criterion IIB', '≥2 of: renal impairment, coagulopathy, liver impairment, ARDS, generalized erythematous macular rash (may desquamate), soft tissue necrosis (NF/myositis/gangrene)'],
['Definite case', 'IA + IIA + IIB'],
['Probable case', 'IB + IIA + IIB (no other etiology)'],
]
story.append(make_table(stss_data, col_widths=[3.5*cm, W-3.5*cm]))
story.append(Spacer(1, 0.2*cm))
story.append(Paragraph(B("Pathophysiology"), h3_style))
for pt in [
"<b>Group A Streptococcus (GAS)</b> = <i>Streptococcus pyogenes</i>",
"Superantigens (SPE-A, SPE-C) → massive polyclonal T-cell activation → cytokine storm (TNF-α, IL-1β, IL-6) → shock, MOF",
"Majority of STSS patients ARE bacteremic (unlike staphylococcal TSS where bacteremia is rare)",
"Associated with: necrotizing fasciitis, myositis, pneumonia",
]:
story.append(Paragraph(f"\u2022 {pt}", bullet_style))
story.append(Paragraph(B("STSS vs. Staphylococcal TSS"), h3_style))
tss_data = [
[B('Feature'), B('STSS (Strep)'), B('TSS (Staph)')],
['Bacteremia', 'Common (majority)', 'Rare'],
['Rash', 'Macular, may desquamate; often absent', 'Diffuse sunburn erythema → desquamation'],
['Primary source', 'Soft tissue (NF, myositis)', 'Tampon, surgical wound, nasal packing'],
['Toxin', 'SPE-A, SPE-C superantigens', 'TSST-1'],
['Mortality', '~30–70%', '~5%'],
]
story.append(make_table(tss_data, col_widths=[4.5*cm, 5.5*cm, 5.5*cm]))
story.append(Spacer(1, 0.2*cm))
story.append(Paragraph(B("Management"), h3_style))
for pt in [
"Aggressive fluid resuscitation (septic shock protocol); vasopressors; ICU admission",
"<b>IV Penicillin G + Clindamycin</b> – combination is KEY: penicillin kills bacteria; clindamycin <b>inhibits toxin production</b>",
"<b>IVIG</b> 1 g/kg day 1, 0.5 g/kg days 2-3 – neutralizes superantigens; consider in severe cases",
"Surgical debridement if associated NF/myositis",
"Notifiable disease",
]:
story.append(Paragraph(f"\u2022 {pt}", bullet_style))
# ─── DRESS ──────────────────────────────────────────────────────────────────
story.append(Paragraph("4. DRESS – DRUG REACTION WITH EOSINOPHILIA AND SYSTEMIC SYMPTOMS", h1_style))
story.append(Paragraph(I("Also called: Drug-Induced Hypersensitivity Syndrome (DIHS)"), body_style))
story.append(Paragraph(B("Causative Drugs"), h3_style))
dress_drugs = [
[B('Category'), B('Drugs')],
['Anticonvulsants', 'Phenytoin, carbamazepine, phenobarbital, lamotrigine, valproate'],
['Antibiotics', 'Sulfonamides, dapsone, minocycline, vancomycin, isoniazid, linezolid, rifampin'],
['Others', 'Allopurinol, amlodipine, captopril, bupropion, celecoxib, ibuprofen'],
]
story.append(make_table(dress_drugs, col_widths=[4*cm, W-4*cm]))
story.append(Paragraph(R("\u26a0 Cross-reactivity among aromatic anticonvulsants (phenytoin, carbamazepine, phenobarbital) is common"), highlight_style))
story.append(Paragraph(B("Pathogenesis"), h3_style))
for pt in [
"Reactive metabolites (hydroxylamine for sulfonamides, arene oxide for anticonvulsants) trigger immune response",
"Activated T-cells release <b>IL-5 → eosinophilia</b>",
"<b>HHV-6 reactivation</b> is hallmark; also HHV-7, EBV, CMV reactivation; CMV reactivation = worse prognosis",
]:
story.append(Paragraph(f"\u2022 {pt}", bullet_style))
story.append(Paragraph(B("Clinical Triad"), h3_style))
story.append(Paragraph(
"1. <b>Morbilliform eruption</b> (diffuse, often with facial edema) "
"2. <b>Eosinophilia</b> (and/or atypical lymphocytosis) "
"3. <b>Systemic organ involvement</b> (hepatitis, nephritis, myocarditis, pneumonitis, thyroiditis)",
ParagraphStyle('Triad', parent=body_style, backColor=colors.HexColor('#eaf0fb'),
borderPadding=(5, 8, 5, 8), leading=16)
))
story.append(Spacer(1, 0.2*cm))
story.append(Paragraph(R("\u26a0 Key feature: Onset 2-8 WEEKS after drug start; persists AFTER drug cessation; recurs on rechallenge"), warning_style))
story.append(Paragraph(B("DRESS vs. SJS/TEN"), h3_style))
dress_ten = [
[B('Feature'), B('DRESS'), B('SJS/TEN')],
['Onset', '2–8 weeks', '1–3 weeks'],
['Mucositis', 'Mild or absent', 'Severe, prominent'],
['Blistering / epidermal detachment', 'Absent', 'Present'],
['Eosinophilia', 'Prominent', 'Usually absent'],
['Lymphadenopathy', 'Common', 'Absent'],
['Herpesvirus reactivation', 'Yes (HHV-6)', 'No'],
['Mortality', '~2–10% (liver failure)', '20–40%'],
]
story.append(make_table(dress_ten, col_widths=[5.5*cm, 4.5*cm, 5.5*cm]))
story.append(Paragraph(B("Management"), h3_style))
for pt in [
"<b>Stop offending drug immediately</b>",
"<b>Systemic corticosteroids: prednisone 1.5-2 mg/kg/day</b>, taper slowly over <b>8-12 weeks</b>",
"Monitor CBC differential, BMP, LFTs throughout taper",
"Steroid-sparing agents if rapid recurrence: mycophenolate mofetil, IVIG, cyclosporine",
"<b>Cardiac evaluation</b> (myocarditis risk) if hypotension or arrhythmia",
"Monitor for late autoimmune sequelae: thyroiditis, lupus, diabetes",
]:
story.append(Paragraph(f"\u2022 {pt}", bullet_style))
# ─── PEMPHIGUS VULGARIS ─────────────────────────────────────────────────────
story.append(Paragraph("5. PEMPHIGUS VULGARIS (PV)", h1_style))
story.append(Paragraph(B("Pathophysiology"), h3_style))
for pt in [
"<b>Autoimmune</b> – IgG antibodies against <b>desmoglein-3 (DSG3)</b> (±DSG1)",
"DSG3 predominant in mucosa and deep epidermis (suprabasal layer)",
"Antibody binding → disruption of desmosomal adhesion → <b>suprabasal acantholysis</b>",
'Histology: Intraepidermal blister with "row of tombstones" (basal cells remain attached to basement membrane)',
"Triggers: penicillamine, captopril (thiol compounds), rifampin, emotional stress",
"More common in Ashkenazi Jews, Mediterranean; age 50-60 years",
]:
story.append(Paragraph(f"\u2022 {pt}", bullet_style))
story.append(Paragraph(B("Clinical Features"), h3_style))
for pt in [
"<b>Oral mucosa first (60-80%)</b> – painful erosions preceding skin disease by months",
"<b>Skin:</b> Non-pruritic, flaccid bullae on normal or erythematous skin; bullae rupture easily → large painful erosions",
"Sites: oral, nasal, esophageal, vulvar, cervical, vaginal, conjunctival",
"<b>Nikolsky sign (+)</b> – lateral pressure on perilesional skin creates new blister",
"<b>Asboe-Hansen sign (+)</b> – pressure on blister top causes lateral extension",
]:
story.append(Paragraph(f"\u2022 {pt}", bullet_style))
story.append(Paragraph(B("Diagnostic Tests"), h3_style))
pv_diag = [
[B('Test'), B('Finding in Pemphigus Vulgaris')],
['Histology', 'Suprabasal acantholysis; "row of tombstones"'],
['Direct Immunofluorescence (DIF)', 'IgG intercellular pattern on keratinocyte surface ("chicken-wire")'],
['Indirect Immunofluorescence (IIF)', 'IgG cell-surface pattern on monkey esophagus substrate'],
['ELISA', 'Anti-DSG3 antibodies (±DSG1); titers correlate with disease activity'],
]
story.append(make_table(pv_diag, col_widths=[6*cm, W-6*cm]))
story.append(Spacer(1, 0.2*cm))
story.append(Paragraph(B("PV vs. Bullous Pemphigoid (BP)"), h3_style))
pv_bp = [
[B('Feature'), B('Pemphigus Vulgaris'), B('Bullous Pemphigoid')],
['Age', '50–60 yr', '>70 yr'],
['Blisters', 'Flaccid, rupture easily', 'Tense, do not rupture easily'],
['Mucosa', 'Commonly involved', 'Usually spared'],
['Cleavage', 'Suprabasal (intraepidermal)', 'Subepidermal'],
['Nikolsky sign', 'Positive', 'Negative'],
['Antigen', 'Desmoglein-3 (±1)', 'BP180, BP230 (hemidesmosomes)'],
['Eosinophils', 'Absent', 'Present (eosinophilic infiltrate)'],
['DIF', 'IgG intercellular', 'IgG + C3 at BMZ (linear)'],
['Prognosis', 'Worse (mortality 5–15% without Rx)', 'Better'],
]
story.append(make_table(pv_bp, col_widths=[4.5*cm, 5.5*cm, 5.5*cm]))
story.append(Paragraph(B("Management"), h3_style))
for pt in [
"<b>First-line:</b> Systemic corticosteroids (prednisone 1-2 mg/kg/day), taper slowly",
"<b>Rituximab</b> (anti-CD20; 1000 mg IV ×2 doses, 2 weeks apart) – now preferred adjunct in moderate-severe disease",
"Steroid-sparing: azathioprine, mycophenolate mofetil",
"Wound care, infection prophylaxis, nutritional support",
"Bisphosphonate + calcium/Vit D if on long-term steroids",
]:
story.append(Paragraph(f"\u2022 {pt}", bullet_style))
# ─── NECROTIZING FASCIITIS ──────────────────────────────────────────────────
story.append(Paragraph("6. NECROTIZING FASCIITIS (NF)", h1_style))
story.append(Paragraph(B("Classification"), h3_style))
nf_class = [
[B('Type'), B('Microbiology'), B('Notes')],
['Type 1 (Polymicrobial)', '≥1 anaerobe + facultative anaerobes (strep, Enterobacteriaceae)', 'Most common; post-surgical, elderly, diabetics'],
['Type 2 (Monomicrobial)', 'GAS most common; S. aureus', 'Any age; young healthy adults'],
['Type 3', 'Gram-negative marine organisms (Vibrio vulnificus, Aeromonas hydrophila)', 'Sea/fresh water injuries'],
['Type 4', 'Fungal (Zygomycosis)', 'Immunocompromised'],
['Fournier Gangrene', 'Polymicrobial', 'Perineal/genital NF'],
]
story.append(make_table(nf_class, col_widths=[3.5*cm, 6.5*cm, 5.5*cm]))
story.append(Paragraph(B("Clinical Features – Classic Progression"), h3_style))
for pt in [
"<b>Pain disproportionate to skin findings</b> – KEY early sign",
"Exquisite tenderness, erythema, warmth, swelling that does NOT respond to antibiotics",
"Skin: shiny, tense → red → <b>purple-gray discoloration</b> within 36 hours",
"<b>Hemorrhagic bullae</b>, violaceous discoloration",
"Thin, watery, malodorous 'dishwater fluid' from wound; necrosis of superficial fascia",
"<b>Paradoxical anesthesia</b> replaces earlier pain (cutaneous nerve destruction)",
"'Woody' induration on palpation",
"Systemic toxicity: fever, leukocytosis, tachycardia, <b>septic shock</b>",
]:
story.append(Paragraph(f"\u2022 {pt}", bullet_style))
story.append(Paragraph(B("LRINEC Score (Laboratory Risk Indicator for Necrotizing Fasciitis)"), h3_style))
lrinec_data = [
[B('Variable'), B('Threshold'), B('Points')],
['CRP', '> 150 mg/L', '+4'],
['WBC', '> 15,000/mm³', '+1\n> 25,000/mm³ → +2'],
['Hemoglobin', '< 13.5 g/dL', '+1\n< 11 g/dL → +2'],
['Sodium', '< 135 mmol/L', '+2'],
['Creatinine', '> 1.6 mg/dL', '+2'],
['Glucose', '> 180 mg/dL', '+1'],
]
story.append(make_table(lrinec_data, col_widths=[4.5*cm, 5*cm, 6*cm]))
story.append(Paragraph(R("\u26a0 Score ≥6 = High risk (PPV 96%). BUT: High specificity, LOW sensitivity. A LOW score does NOT exclude NF. Cannot use in isolation. Misses Vibrio NF and neck NF."), warning_style))
story.append(Paragraph(B("Imaging"), h3_style))
for pt in [
"<b>MRI with contrast:</b> Best modality – fascial thickening, gas tracking along fascia; <i>absence of MRI abnormalities virtually rules out NF</i>",
"<b>CT with contrast:</b> Fascial thickening; gas in soft tissue (highly specific but present in only 1/3 cases)",
"<b>Plain X-ray:</b> May show soft tissue gas",
R("\u26a0 Do NOT delay surgery for imaging if clinical suspicion is high"),
]:
story.append(Paragraph(f"\u2022 {pt}", bullet_style))
story.append(Paragraph(B("Finger Test (Surgical Gold Standard)"), h3_style))
story.append(Paragraph(
"Small bedside incision → probe along fascial plane: <b>No bleeding + 'dishwater fluid' + lack of tissue resistance to blunt dissection = NF confirmed</b>",
ParagraphStyle('Finger', parent=body_style, backColor=colors.HexColor('#eaf0fb'),
borderPadding=(5, 8, 5, 8))
))
story.append(Paragraph(B("Management"), h3_style))
for pt in [
"<b>Emergency surgical debridement (fasciotomy)</b> – mainstay; repeat at 24 hrs; amputation may be necessary",
"<b>Empiric antibiotics:</b> <b>Vancomycin + Piperacillin/Tazobactam</b> (standard broad spectrum)",
"Penicillin allergy: ciprofloxacin + metronidazole or clindamycin",
"Add <b>clindamycin</b> for GAS-dominant infections (inhibits toxin production)",
"IVIG – some benefit in GAS-associated NF (neutralizes superantigens)",
"Aggressive fluid resuscitation, vasopressors if shock; ICU",
"<b>Mortality: 20-60%</b>; higher with delay to debridement, female sex, older age, GAS, elevated creatinine/lactate",
]:
story.append(Paragraph(f"\u2022 {pt}", bullet_style))
# ─── MASTER COMPARISON TABLE ─────────────────────────────────────────────────
story.append(PageBreak())
story.append(Paragraph("MASTER COMPARISON TABLE", h1_style))
master = [
[B('Feature'), B('TEN'), B('SSSS'), B('STSS'), B('DRESS'), B('Pemphigus\nVulgaris'), B('NF')],
['Cause', 'Drug (T-cell)', 'S. aureus\nexotoxin', 'GAS\nsuperantigen', 'Drug +\nHHV-6', 'IgG vs\nDsg3', 'Polymicro-\nbial/GAS'],
['Blister\nplane', 'Full-thick-\nness necrosis', 'Granular\nlayer (super-\nficial)', 'N/A\n(shock)', 'No\nblistering', 'Suprabasal\n(intrapider-\nmal)', 'Late bullae\n(hemorrh.)'],
['Mucosa', 'YES\n(>90%)', 'NO', 'Variable', 'Mild/\nabsent', 'YES\n(early)', 'Absent'],
['Nikolsky', 'Positive', 'Positive', 'N/A', 'Negative', 'Positive', 'N/A'],
['Key lab', 'SCORTEN;\nbiopsy', 'Frozen\nsection', 'GAS culture;\nTroponin', 'Eosinophilia;\nHHV-6;\nLFTs', 'Anti-Dsg3\nELISA; DIF', 'LRINEC;\nCK; MRI'],
['Mortality', '30–50%', '<5% (kids)', '30–70%', '2–10%', '5–15%', '20–60%'],
['First Rx', 'Stop drug;\ncyclosporine', 'IV Vanco-\nmycin', 'PenG +\nClindamycin', 'Stop drug;\nsteroids', 'Steroids +\nRituximab', 'Surgical\ndebride-\nment'],
]
master_t = Table(master, colWidths=[2.5*cm, 2.4*cm, 2.4*cm, 2.4*cm, 2.4*cm, 2.4*cm, 2.4*cm], repeatRows=1)
master_t.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,0), colors.HexColor('#1a3a5c')),
('TEXTCOLOR', (0,0), (-1,0), colors.white),
('FONTNAME', (0,0), (-1,0), 'Helvetica-Bold'),
('FONTSIZE', (0,0), (-1,-1), 7.5),
('FONTNAME', (0,1), (-1,-1), 'Helvetica'),
('ALIGN', (0,0), (-1,-1), 'LEFT'),
('VALIGN', (0,0), (-1,-1), 'TOP'),
('ROWBACKGROUNDS', (0,1), (-1,-1), [colors.white, colors.HexColor('#eaf0fb')]),
('GRID', (0,0), (-1,-1), 0.4, colors.HexColor('#b0b8c8')),
('TOPPADDING', (0,0), (-1,-1), 4),
('BOTTOMPADDING', (0,0), (-1,-1), 4),
('LEFTPADDING', (0,0), (-1,-1), 4),
('RIGHTPADDING', (0,0), (-1,-1), 4),
('BACKGROUND', (0,1), (0,-1), colors.HexColor('#d9e6f2')),
('FONTNAME', (0,1), (0,-1), 'Helvetica-Bold'),
]))
story.append(master_t)
# ─── HIGH-YIELD EXAM TIPS ────────────────────────────────────────────────────
story.append(Paragraph("HIGH-YIELD EXAM TIPS", h1_style))
tips = [
("1", "Nikolsky sign", "Positive in TEN, SSSS, and Pemphigus Vulgaris – <b>NEGATIVE in Bullous Pemphigoid</b>"),
("2", "SSSS – No mucosa", "Mucosa spared because Dsg1 is absent from mucosa (the toxin's target)"),
("3", "Frozen section", "Distinguishes SSSS (superficial cleavage) from TEN (full-thickness necrosis) – rapid bedside test"),
("4", "STSS bacteremia", "STSS is usually <i>bacteremic</i>; staphylococcal TSS usually is NOT"),
("5", "DRESS onset", "Always 2-8 WEEKS delayed – this is the pathognomonic differentiator from other drug rashes"),
("6", "HHV-6 = DRESS", "HHV-6 reactivation is hallmark of DRESS – not seen in SJS/TEN"),
("7", "Pain out of proportion = NF", "Severe pain disproportionate to skin findings = NF until proven otherwise"),
("8", "LRINEC limitation", "High specificity but LOW sensitivity – cannot be used to <i>exclude</i> NF"),
("9", "Clindamycin in NF/STSS", "Added for GAS infections to inhibit toxin production (not just as antibiotic)"),
("10", "Lynch algorithm flow", "Morphology first → fluid-filled vs solid → if vesiculo-bullous: assess cleavage plane + mucosal involvement"),
]
tips_data = [[B('#'), B('Key Point'), B('Detail')]] + [[t[0], B(t[1]), t[2]] for t in tips]
story.append(make_table(
tips_data,
col_widths=[0.8*cm, 4.5*cm, W-5.3*cm],
header_bg=colors.HexColor('#c0392b'),
alt_bg=colors.HexColor('#fef5f5')
))
story.append(Spacer(1, 0.5*cm))
story.append(HRFlowable(width="100%", thickness=1, color=colors.HexColor('#b0b8c8'), spaceAfter=6))
story.append(Paragraph(
"Sources: Harrison's Principles of Internal Medicine 22E | Tintinalli's Emergency Medicine | Dermatology 2-Volume Set 5e | "
"Fitzpatrick's Dermatology | Goldman-Cecil Medicine | Andrews' Diseases of the Skin | Cummings Otolaryngology",
caption_style
))
doc.build(story)
print(f"PDF saved to: {output_path}")
PYEOF
echo "Script written"
show command