tuberculosis complete with abdominal and tbm

| Parameter | Finding |
|---|---|
| Character | Exudative |
| WBC count | 50-10,000 leukocytes/μL, predominantly lymphocytes |
| AFB smear | Rarely positive |
| Culture | ~50% positive (increases with large volume) |
| SAAG | Low (<1.1 g/dL) in non-cirrhotic; high in cirrhotic (cirrhosis dominates) |
| ADA | High sensitivity 93-100%, specificity 96-97%; cut-off 36-40 IU/L (optimal 39 IU/L) |

| Parameter | Finding in TBM |
|---|---|
| Opening pressure | Elevated |
| Leukocytes | Up to 1000/μL (up to 1500); lymphocyte predominance; PMNs may predominate early |
| Protein | Elevated 1-8 g/L (100-800 mg/dL) |
| Glucose | Low (typically) |
| AFB smear | Infrequently positive (repeated LPs increase yield) |
| Culture | Positive up to 80% - gold standard |
| Xpert MTB/RIF | Sensitivity up to 80% - preferred initial test |

| Phase | Duration | Drugs | Notes |
|---|---|---|---|
| Intensive | 2 months | HRZE - Isoniazid (H) + Rifampicin (R) + Pyrazinamide (Z) + Ethambutol (E) | 4-drug regimen |
| Continuation | 4 months | HR - Isoniazid + Rifampicin | 85% global success rate |
| Drug | Dose | Key Side Effects |
|---|---|---|
| Isoniazid (INH) | 5 mg/kg/day (single dose) | Peripheral neuropathy, hepatitis (give pyridoxine 50 mg/day to prevent neuropathy) |
| Rifampin | 10 mg/kg/day | Hepatitis, drug interactions, orange discoloration of secretions |
| Pyrazinamide | 20-35 mg/kg/day | Hepatitis, rash, GI upset, hyperuricemia |
| Ethambutol | 15 mg/kg/day | Optic neuropathy (visual acuity + red-green color discrimination monitoring required) |
| Ethionamide (MDR-TB) | 15-25 mg/kg/day (divided doses) | Gastric irritation, optic neuropathy |
| Test | Pulmonary TB | Abdominal/Peritoneal TB | TBM |
|---|---|---|---|
| TST/IGRA | Positive in active disease | Positive | Positive |
| Smear (AFB) | Positive or negative | Rarely positive (ascitic fluid ~3%) | Rarely positive (CSF) |
| Culture | Gold standard | ~50% ascitic fluid; best with laparoscopic biopsy | Up to 80% CSF |
| Xpert MTB/RIF | Preferred initial test | Equivocal for pericardial/peritoneal | Sensitivity up to 80%; preferred initial test |
| ADA | Pleural fluid (high sensitivity) | Ascitic fluid ≥39 IU/L (sensitivity 100%, specificity 97%) | CSF ADA elevated |
| Imaging | CXR: cavitation, upper lobe; CT: miliary | CT: LAD, omental thickening, ascites | CT/MRI: basal enhancement, hydrocephalus, infarction |
| Laparoscopy/Biopsy | - | Best diagnostic test for peritoneal TB | LP is cornerstone |
Search for latest WHO TB treatment guidelines and drug-resistant TB protocols
WHO tuberculosis treatment guidelines 2024 2025 update drug resistant
"tuberculosis" AND "treatment guidelines" AND "drug resistant"
WHO DR-TB MDR-TB XDR-TB BPaLM regimen 2025 guidelines update
https://www.who.int/teams/global-programme-on-tuberculosis-a…
"tuberculosis" AND "BPaLM" OR "bedaquiline pretomanid linezolid"
https://clinician.nejm.org/management-drug-susceptible-drug-…
PMID: 39813501
PMID: 37980911
WHO 2025 consolidated TB guidelines Module 4 drug susceptible 4-month regimen rifapentine moxifloxacin
| Phase | Duration | Drugs | Notation |
|---|---|---|---|
| Intensive | 2 months | Isoniazid + Rifampin + Pyrazinamide + Ethambutol | 2HRZE |
| Continuation | 4 months | Isoniazid + Rifampin | 4HR |
| Phase | Duration | Drugs | Notation |
|---|---|---|---|
| Intensive | 2 months | Isoniazid + Rifapentine + Moxifloxacin + Pyrazinamide | 2HPMZ |
| Continuation | 2 months | Isoniazid + Rifapentine + Moxifloxacin | 2HPM |
Note: A 2025 commentary in AJRCCM (Wilson et al., Mayo Clinic) urges caution in broad adoption of the 4-month regimen pending real-world evidence outside controlled trial settings.
| Classification | Resistance Pattern |
|---|---|
| Isoniazid-resistant TB (Hr-TB) | Isoniazid-resistant, rifampicin-susceptible |
| RR-TB | Rifampicin-resistant (any pattern) |
| MDR-TB | Resistant to both isoniazid + rifampicin |
| Pre-XDR-TB | MDR/RR-TB + resistance to any fluoroquinolone |
| XDR-TB | MDR/RR-TB + resistance to any fluoroquinolone + ≥1 of bedaquiline or linezolid |
| Regimen | Drugs | Duration |
|---|---|---|
| BPaLM | Bedaquiline + Pretomanid + Linezolid + Moxifloxacin | 6 months |
| BDLLfx (NEW in 2025) | Bedaquiline + Delamanid + Linezolid + Levofloxacin + Clofazimine | 6 months |
| Regimen | Drugs | Duration |
|---|---|---|
| BPaL | Bedaquiline + Pretomanid + Linezolid | 6 months |
| BDLC | Bedaquiline + Delamanid + Linezolid + Clofazimine | 6 months |
| Letter | Drug |
|---|---|
| B | Bedaquiline |
| Pa | Pretomanid |
| L | Linezolid |
| M | Moxifloxacin |
| D | Delamanid |
| Lfx | Levofloxacin |
| C | Clofazimine |
| H | Isoniazid |
| R | Rifampicin |
| Z | Pyrazinamide |
| E | Ethambutol |
| P | Rifapentine |
| Year | Countries using 6-month MDR/RR-TB regimens | People started on 6-month regimens |
|---|---|---|
| 2022 | 41 | 1,744 |
| 2023 | 60 | 5,653 |
| 2024 | 97 | 34,256 |
| Feature | DS-TB (Standard) | DS-TB (New) | MDR/RR-TB (Preferred) | Pre-XDR/XDR-TB |
|---|---|---|---|---|
| Duration | 6 months | 4 months | 6 months | 6-9 months (individualized 18-20 months for XDR) |
| Key drugs | HRZE / HR | HPMZ / HPM | BPaLM or BDLLfx | BPaL or BDLC |
| Route | Oral | Oral | All-oral | All-oral (injectable as last resort) |
| Guideline year | Classic | 2025 | 2022-2025 | 2025 |
arrange all this material in on single pdf including treatment
ls -la /home/daytona/workspace/ 2>/dev/null || echo "workspace empty or not found"
python3 -c "import reportlab; print('reportlab ok')" && python3 -c "from reportlab.lib.pagesizes import A4; print('A4 ok')"
mkdir -p /home/daytona/workspace/tb-pdf
~/tb-pdf/generate_tb_pdf.py
from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.units import cm, mm
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.enums import TA_LEFT, TA_CENTER, TA_JUSTIFY
from reportlab.platypus import (
SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
HRFlowable, PageBreak, KeepTogether
)
from reportlab.platypus.tableofcontents import TableOfContents
from reportlab.lib.colors import HexColor
import datetime
# ── Colour palette ──────────────────────────────────────────────────────────
C_NAVY = HexColor("#1A3557")
C_TEAL = HexColor("#1D7A8A")
C_MINT = HexColor("#E8F5F7")
C_AMBER = HexColor("#F5A623")
C_RED = HexColor("#C0392B")
C_GREEN = HexColor("#27AE60")
C_LGRAY = HexColor("#F4F6F8")
C_DGRAY = HexColor("#2C3E50")
C_WHITE = colors.white
C_BLACK = colors.black
C_WARN = HexColor("#FFF3CD")
C_WARNBR = HexColor("#856404")
W, H = A4
# ── Styles ───────────────────────────────────────────────────────────────────
base = getSampleStyleSheet()
def make_style(name, parent="Normal", **kw):
s = ParagraphStyle(name, parent=base[parent], **kw)
return s
sTitle = make_style("sTitle",
fontSize=28, textColor=C_WHITE, fontName="Helvetica-Bold",
alignment=TA_CENTER, leading=36, spaceAfter=4)
sSubtitle = make_style("sSubtitle",
fontSize=13, textColor=HexColor("#B0D4E0"), fontName="Helvetica",
alignment=TA_CENTER, leading=18)
sH1 = make_style("sH1",
fontSize=16, textColor=C_WHITE, fontName="Helvetica-Bold",
leading=20, spaceBefore=4, spaceAfter=4)
sH2 = make_style("sH2",
fontSize=13, textColor=C_NAVY, fontName="Helvetica-Bold",
leading=17, spaceBefore=10, spaceAfter=4,
borderPad=4)
sH3 = make_style("sH3",
fontSize=11, textColor=C_TEAL, fontName="Helvetica-Bold",
leading=15, spaceBefore=7, spaceAfter=3)
sBody = make_style("sBody",
fontSize=9.5, textColor=C_DGRAY, fontName="Helvetica",
leading=14, spaceAfter=5, alignment=TA_JUSTIFY)
sBullet = make_style("sBullet",
fontSize=9.5, textColor=C_DGRAY, fontName="Helvetica",
leading=14, spaceAfter=3, leftIndent=14, firstLineIndent=-10)
sSmall = make_style("sSmall",
fontSize=8, textColor=HexColor("#666666"), fontName="Helvetica",
leading=11, spaceAfter=4, alignment=TA_CENTER)
sWarn = make_style("sWarn",
fontSize=9, textColor=C_WARNBR, fontName="Helvetica-Bold",
leading=13, spaceAfter=4, leftIndent=8)
sLabel = make_style("sLabel",
fontSize=8, textColor=C_WHITE, fontName="Helvetica-Bold",
leading=11, alignment=TA_CENTER)
sFooter = make_style("sFooter",
fontSize=7.5, textColor=HexColor("#999999"), fontName="Helvetica",
leading=10, alignment=TA_CENTER)
# ── Helper builders ──────────────────────────────────────────────────────────
def section_header(title, color=C_NAVY):
"""Full-width coloured section header bar."""
tbl = Table([[Paragraph(title, sH1)]], colWidths=[W - 4*cm])
tbl.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), color),
("TOPPADDING", (0,0), (-1,-1), 8),
("BOTTOMPADDING", (0,0), (-1,-1), 8),
("LEFTPADDING", (0,0), (-1,-1), 14),
("RIGHTPADDING", (0,0), (-1,-1), 14),
("ROUNDEDCORNERS", [4]),
]))
return tbl
def info_box(title, items, bg=C_MINT, border=C_TEAL):
"""Shaded info box with bullet items."""
content = [Paragraph(f"<b>{title}</b>", make_style("_ib",
fontSize=9.5, textColor=C_NAVY, fontName="Helvetica-Bold", leading=14))]
for item in items:
content.append(Paragraph(f"• {item}", sBullet))
tbl = Table([[content]], colWidths=[W - 4*cm])
tbl.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), bg),
("TOPPADDING", (0,0), (-1,-1), 8),
("BOTTOMPADDING", (0,0), (-1,-1), 8),
("LEFTPADDING", (0,0), (-1,-1), 12),
("RIGHTPADDING", (0,0), (-1,-1), 12),
("LINEABOVE", (0,0), (-1,0), 2, border),
("LINEBELOW", (0,-1), (-1,-1), 0.5, border),
("LINEBEFORE", (0,0), (0,-1), 3, border),
]))
return tbl
def warn_box(text, bg=C_WARN, border=C_AMBER):
tbl = Table([[Paragraph(f"⚠ {text}", sWarn)]], colWidths=[W - 4*cm])
tbl.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),
("LINEBEFORE", (0,0), (0,-1), 4, border),
("ROUNDEDCORNERS", [3]),
]))
return tbl
def drug_table(header_row, rows, col_w=None):
data = [header_row] + rows
if col_w is None:
col_w = [(W - 4*cm) / len(header_row)] * len(header_row)
tbl = Table(data, colWidths=col_w, repeatRows=1)
style = [
("BACKGROUND", (0,0), (-1,0), C_NAVY),
("TEXTCOLOR", (0,0), (-1,0), C_WHITE),
("FONTNAME", (0,0), (-1,0), "Helvetica-Bold"),
("FONTSIZE", (0,0), (-1,-1), 8.5),
("LEADING", (0,0), (-1,-1), 12),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING", (0,0), (-1,-1), 5),
("LEFTPADDING", (0,0), (-1,-1), 6),
("RIGHTPADDING", (0,0), (-1,-1), 6),
("ROWBACKGROUNDS",(0,1), (-1,-1), [C_WHITE, C_LGRAY]),
("GRID", (0,0), (-1,-1), 0.4, HexColor("#CCCCCC")),
("ALIGN", (0,0), (-1,-1), "LEFT"),
("VALIGN", (0,0), (-1,-1), "MIDDLE"),
]
tbl.setStyle(TableStyle(style))
return tbl
def p(text, style=sBody):
return Paragraph(text, style)
def b(text):
return Paragraph(f"• {text}", sBullet)
def sp(n=6):
return Spacer(1, n)
def hr(color=C_TEAL, thickness=0.5):
return HRFlowable(width="100%", thickness=thickness, color=color, spaceAfter=4, spaceBefore=4)
# ── Cover page ───────────────────────────────────────────────────────────────
def cover_page():
elems = []
# Blue cover banner
cover_data = [[
Paragraph("TUBERCULOSIS", sTitle),
]]
cover_tbl = Table(cover_data, colWidths=[W - 4*cm])
cover_tbl.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), C_NAVY),
("TOPPADDING", (0,0), (-1,-1), 40),
("BOTTOMPADDING", (0,0), (-1,-1), 10),
("LEFTPADDING", (0,0), (-1,-1), 10),
("RIGHTPADDING", (0,0), (-1,-1), 10),
]))
elems.append(cover_tbl)
sub_data = [[
Paragraph("Complete Clinical Reference", sSubtitle),
]]
sub_tbl = Table(sub_data, colWidths=[W - 4*cm])
sub_tbl.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), C_NAVY),
("TOPPADDING", (0,0), (-1,-1), 0),
("BOTTOMPADDING", (0,0), (-1,-1), 10),
("LEFTPADDING", (0,0), (-1,-1), 10),
("RIGHTPADDING", (0,0), (-1,-1), 10),
]))
elems.append(sub_tbl)
sub2_data = [[
Paragraph("Pulmonary TB · Abdominal TB · TB Meningitis · Drug-Resistant TB · 2025 WHO & ATS/CDC/ERS/IDSA Guidelines", sSubtitle),
]]
sub2_tbl = Table(sub2_data, colWidths=[W - 4*cm])
sub2_tbl.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), C_NAVY),
("TOPPADDING", (0,0), (-1,-1), 0),
("BOTTOMPADDING", (0,0), (-1,-1), 30),
("LEFTPADDING", (0,0), (-1,-1), 10),
("RIGHTPADDING", (0,0), (-1,-1), 10),
]))
elems.append(sub2_tbl)
elems.append(sp(20))
# Cover key stats boxes
stats = [
("10 M+", "New TB cases\nglobally per year"),
("1.5 M", "Deaths annually\n(TB is #1 infectious killer)"),
("25%", "World population\nhas latent TB"),
("500K", "New MDR/RR-TB\ncases per year"),
]
stat_cells = []
for val, label in stats:
cell_content = [
Paragraph(f'<font color="#F5A623"><b>{val}</b></font>',
make_style("_sv", fontSize=20, fontName="Helvetica-Bold",
alignment=TA_CENTER, leading=24, textColor=C_AMBER)),
Paragraph(label,
make_style("_sl", fontSize=8, fontName="Helvetica",
alignment=TA_CENTER, leading=11, textColor=HexColor("#CCDDEE"))),
]
stat_cells.append(cell_content)
stat_tbl = Table([stat_cells], colWidths=[(W - 4*cm)/4]*4)
stat_tbl.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), C_DGRAY),
("TOPPADDING", (0,0), (-1,-1), 14),
("BOTTOMPADDING", (0,0), (-1,-1), 14),
("ALIGN", (0,0), (-1,-1), "CENTER"),
("VALIGN", (0,0), (-1,-1), "MIDDLE"),
("LINEBEFORE", (1,0), (-1,-1), 0.5, HexColor("#445566")),
]))
elems.append(stat_tbl)
elems.append(sp(30))
date_str = datetime.date.today().strftime("%B %Y")
elems.append(p(
f"Sources: Goldman-Cecil Medicine 26e · Harrison's Principles 22e (2025) · Adams & Victor's Neurology 12e · "
f"Murray & Nadel's Respiratory Medicine · Yamada's Gastroenterology 7e · "
f"WHO Consolidated TB Guidelines Module 4 (2025) · ATS/CDC/ERS/IDSA 2025 Update · "
f"TB-PRACTECAL Trial (Lancet Respir Med, 2024) · WHO Global TB Report 2025",
sSmall
))
elems.append(p(f"Compiled: {date_str}", sSmall))
elems.append(PageBreak())
return elems
# ── Table of Contents page ───────────────────────────────────────────────────
def toc_page():
elems = []
elems.append(section_header("TABLE OF CONTENTS", C_TEAL))
elems.append(sp(10))
toc_items = [
("1.", "Overview and Epidemiology", "3"),
("2.", "Pulmonary Tuberculosis", "3"),
(" 2.1", "Pathogenesis and Disease Spectrum", "3"),
(" 2.2", "Clinical Features", "4"),
(" 2.3", "Radiology", "4"),
(" 2.4", "TB Pleuritis", "4"),
("3.", "Abdominal Tuberculosis", "5"),
(" 3.1", "Gastrointestinal (Intestinal) TB", "5"),
(" 3.2", "Peritoneal TB", "6"),
("4.", "Tuberculous Meningitis (TBM)", "7"),
(" 4.1", "Pathogenesis and Pathology", "7"),
(" 4.2", "Clinical Features", "7"),
(" 4.3", "CSF Findings", "8"),
(" 4.4", "Neuroradiology", "8"),
(" 4.5", "Tuberculoma", "8"),
("5.", "Diagnosis Summary", "9"),
("6.", "Treatment", "10"),
(" 6.1", "Drug-Susceptible TB — Standard 6-Month Regimen", "10"),
(" 6.2", "NEW: 4-Month Regimen (2025 Update)", "10"),
(" 6.3", "Drug Dosing and Side Effects", "11"),
("7.", "Drug-Resistant TB Treatment (WHO 2025)", "12"),
(" 7.1", "Definitions", "12"),
(" 7.2", "6-Month All-Oral Regimens (Preferred)", "12"),
(" 7.3", "9-Month and 18-20 Month Regimens", "13"),
(" 7.4", "Key Trial Evidence", "13"),
("8.", "Special Situations", "14"),
(" 8.1", "TB-HIV Coinfection", "14"),
(" 8.2", "TB Meningitis Treatment", "14"),
(" 8.3", "TB in Pregnancy", "14"),
("9.", "Other Extrapulmonary Manifestations", "15"),
("10.", "2025 WHO Nutrition Guidelines (Module 6)", "15"),
]
toc_data = [[
Paragraph(f"<b>{n}</b>", make_style("_tn", fontSize=9, fontName="Helvetica-Bold", textColor=C_NAVY, leading=13)),
Paragraph(title, make_style("_tt", fontSize=9, fontName="Helvetica", textColor=C_DGRAY, leading=13)),
Paragraph(pg, make_style("_tp", fontSize=9, fontName="Helvetica", textColor=C_TEAL, leading=13, alignment=TA_CENTER)),
] for n, title, pg in toc_items]
toc_tbl = Table(toc_data, colWidths=[1.2*cm, 13.5*cm, 1.3*cm])
toc_tbl.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),
("ROWBACKGROUNDS",(0,0), (-1,-1), [C_WHITE, C_LGRAY]),
("LINEBELOW", (0,-1), (-1,-1), 0.5, HexColor("#DDDDDD")),
("VALIGN", (0,0), (-1,-1), "MIDDLE"),
]))
elems.append(toc_tbl)
elems.append(PageBreak())
return elems
# ── Section 1: Epidemiology ──────────────────────────────────────────────────
def section1():
e = []
e.append(section_header("1. OVERVIEW AND EPIDEMIOLOGY"))
e.append(sp(8))
e.append(p("Tuberculosis (TB) is caused by <i>Mycobacterium tuberculosis</i>, an obligate intracellular aerobe and acid-fast bacillus. It remains the world's leading infectious disease killer, with approximately <b>10 million new cases</b> and <b>1.5 million deaths</b> per year globally. HIV coinfection accounts for ~9% of new cases; TB incidence in persons with full-blown HIV is nearly <b>500 times</b> that of the general population."))
e.append(sp(6))
epid_data = [
[Paragraph("<b>Parameter</b>", sLabel), Paragraph("<b>Data</b>", sLabel)],
["New TB cases/year", "~10 million"],
["Deaths/year", "1.5 million"],
["Latent TB (world population)", "~25%"],
["Lifetime reactivation risk", "5–15%"],
["EPTB proportion", "15% of all TB cases (range 8–24%)"],
["HIV coinfection", "~9% of new cases"],
["MDR/RR-TB new cases/year", "~500,000"],
["GI TB (% of EPTB)", "15–25% of EPTB; ileocecal region most common (64%)"],
["CNS TB (% of EPTB)", "~5% in USA"],
]
tbl = drug_table(epid_data[0], [
[Paragraph(str(r[0]), sBody), Paragraph(str(r[1]), sBody)] for r in epid_data[1:]
], col_w=[8*cm, 8*cm])
e.append(tbl)
e.append(sp(8))
e.append(info_box("EPTB Most Common Sites (in order)", [
"Lymphatic nodes",
"Pleura",
"Skeletal system",
"Genitourinary tract",
"Gastrointestinal tract",
"Central nervous system",
]))
e.append(sp(8))
e.append(info_box("Risk Factors for TB Reactivation / EPTB", [
"HIV infection (CD4 <200 cells/mm³ = greatest risk)",
"Diabetes mellitus, malignancies, renal impairment",
"TNF-α antagonists (infliximab, etanercept) — screen before starting",
"Corticosteroids ≥15 mg/day prednisone for ≥1 month",
"Malnutrition, hypoalbuminaemia",
"Birth/residence in high-incidence country (>100/100,000/yr)",
"Homelessness, incarceration, close contact with infectious case",
"Female gender, younger age, certain bacterial genotypes (for EPTB)",
]))
return e
# ── Section 2: Pulmonary TB ──────────────────────────────────────────────────
def section2():
e = []
e.append(PageBreak())
e.append(section_header("2. PULMONARY TUBERCULOSIS"))
e.append(sp(8))
e.append(p("<b>2.1 Pathogenesis and Disease Spectrum</b>", sH2))
e.append(p("After initial infection, the outcome depends on the host immune response. The spectrum ranges from bacterial elimination to active disease. The Mantoux and IGRA tests cannot distinguish latent from active disease — clinical, microbiological, and radiological assessment is required."))
e.append(sp(6))
spectrum_data = [
[Paragraph("<b>Stage</b>", sLabel), Paragraph("<b>TST/IGRA</b>", sLabel), Paragraph("<b>Culture</b>", sLabel), Paragraph("<b>Symptoms</b>", sLabel), Paragraph("<b>Infectious</b>", sLabel)],
["Uninfected", "Negative", "Negative", "None", "No"],
["Latent TB infection", "Positive", "Negative", "None", "No"],
["Subclinical disease", "Positive", "Intermittent +ve", "Mild or none", "Sporadically"],
["Active TB disease", "Usually positive", "Positive", "Mild to severe", "Yes"],
]
tbl = drug_table(spectrum_data[0], [
[Paragraph(str(r[i]), sBody) for i in range(5)] for r in spectrum_data[1:]
], col_w=[3.5*cm, 2.8*cm, 2.8*cm, 3*cm, 2.4*cm])
e.append(tbl)
e.append(sp(8))
e.append(p("<b>2.2 Clinical Features</b>", sH2))
e.append(p("<b>Symptoms:</b>"))
for item in [
"Persistent cough — productive or nonproductive (most common; absent in up to 25% of culture-confirmed cases)",
"Fever, chills, night sweats, weight loss",
"Haemoptysis — occurs in advanced cavitary disease",
"Fatigue, anorexia, malaise",
]:
e.append(b(item))
e.append(sp(4))
e.append(p("<b>Physical examination:</b>"))
for item in [
"Post-tussive rales in upper lung zones",
"Amphoric breath sounds — indicates a cavity",
"Lymphadenopathy — uncommon in immunocompetent adults (common in primary TB of children)",
]:
e.append(b(item))
e.append(sp(8))
e.append(p("<b>2.3 Chest Radiology</b>", sH2))
radio_data = [
[Paragraph("<b>Type</b>", sLabel), Paragraph("<b>Radiological Features</b>", sLabel)],
["Primary TB", "Parenchymal consolidation (any lobe), hilar/paratracheal lymphadenopathy, pleural effusion; Ghon focus + Ranke complex"],
["Post-primary (Reactivation)", "Upper lobe predominance, cavitation, fibrosis, volume loss, bronchogenic spread"],
["Miliary TB", "Diffuse 1–2 mm 'millet-seed' nodules bilaterally — haematogenous spread"],
["TB Pleuritis", "Unilateral exudative pleural effusion; often without parenchymal changes"],
]
tbl = drug_table(radio_data[0], [
[Paragraph(str(r[0]), sBody), Paragraph(str(r[1]), sBody)] for r in radio_data[1:]
], col_w=[4.5*cm, 12*cm])
e.append(tbl)
e.append(sp(8))
e.append(p("<b>2.4 TB Pleuritis</b>", sH2))
e.append(p("TB pleuritis results from a <b>type IV (delayed) hypersensitivity reaction</b> triggered by release of TB antigens from rupture of subpleural disease into the pleural space. Fulminant TB empyema (direct infection) is much rarer."))
e.append(sp(4))
for item in [
"Pleural fluid: exudative, predominantly lymphocytic",
"10% of patients develop restrictive spirometric defects after a median 23 months follow-up",
"Corticosteroids: may improve residual pleural changes on CXR but do <b>not</b> improve long-term respiratory function and increase adverse events (Cochrane)",
"Early pleural drainage may reduce breathlessness and limit lung function impairment at 12 months",
]:
e.append(b(item))
return e
# ── Section 3: Abdominal TB ──────────────────────────────────────────────────
def section3():
e = []
e.append(PageBreak())
e.append(section_header("3. ABDOMINAL TUBERCULOSIS"))
e.append(sp(8))
e.append(p("Abdominal TB encompasses three main compartments: <b>gastrointestinal (intestinal) TB</b>, <b>peritoneal TB</b>, and <b>lymph node TB</b>. GI TB accounts for 3.5% of EPTB in the USA."))
e.append(sp(8))
# 3.1 Intestinal TB
e.append(p("<b>3.1 Gastrointestinal (Intestinal) Tuberculosis</b>", sH2))
e.append(p("<b>Sites of involvement (% of GI TB cases):</b>"))
sites_data = [
[Paragraph("<b>Site</b>", sLabel), Paragraph("<b>Frequency</b>", sLabel), Paragraph("<b>Notes</b>", sLabel)],
["Ileocecal region", "64% (most common)", "Terminal ileum + cecum; produces obstructive picture"],
["Jejunum", "Next most common", "Proximal lesions are rare"],
["Colon", "Less common", "Colonic TB can mimic malignancy"],
["Mouth to anus", "Any site possible", "Lesions proximal to terminal ileum are unusual"],
]
tbl = drug_table(sites_data[0], [
[Paragraph(str(r[i]), sBody) for i in range(3)] for r in sites_data[1:]
], col_w=[4*cm, 4*cm, 8.5*cm])
e.append(tbl)
e.append(sp(8))
e.append(p("<b>Pathogenesis routes:</b>"))
for item in [
"Swallowing of infected sputum (direct mucosal seeding) — primary route in pulmonary TB",
"Haematogenous spread",
"Lymphatic spread",
"Direct spread from adjacent structures (lymph nodes, fallopian tubes, Pott's disease)",
"<i>M. bovis</i> from unpasteurised dairy products (developing nations)",
]:
e.append(b(item))
e.append(sp(6))
e.append(p("<b>Clinical Features:</b>"))
e.append(p("Only 15–25% of GI TB cases have concomitant pulmonary TB."))
for item in [
"Abdominal pain + GI obstruction — most common presentation",
"Fever, nausea, diarrhoea, weight loss, anorexia",
"GI bleeding",
"Palpable mass in the ileocecal region",
"Can mimic <b>Crohn's disease</b>, acute appendicitis, or colorectal carcinoma",
]:
e.append(b(item))
e.append(sp(6))
e.append(p("<b>Differential Diagnosis of Intestinal TB:</b>", sH3))
dd_items = [
"Crohn's disease (key mimic — granulomas are noncaseating, <400 μm, poorly organised)",
"Acute appendicitis",
"Malignancies (lymphoma, carcinoma)",
"NSAIDs-related enteropathy",
"Sarcoidosis, Amyloidosis",
"Salmonellosis, Yersiniosis, CMV, Histoplasmosis",
"MAC (M. avium complex) enteritis — especially in HIV",
"Actinomycosis, Anisakiasis, Typhlitis, Eosinophilic enteritis",
"Whipple's disease, Vasculitides, Ischaemia",
]
e.append(info_box("Differential Diagnosis of Intestinal TB", dd_items, bg=HexColor("#FFF5E6"), border=C_AMBER))
e.append(sp(8))
e.append(p("<b>Diagnosis:</b>"))
for item in [
"AFB smear of tissue (Ziehl-Neelsen) — positive in only ~3% of proven cases",
"AFB culture — gold standard but takes 4–8 weeks; <40% of patients have positive cultures",
"Endoscopic biopsy: caseating granulomas on histology",
"PCR / Xpert MTB/RIF: sensitivity 50–100%, specificity 62–97% (further validation needed)",
"CD4+ T-cell activation markers (CD38, HLA-DR, Ki67+) — emerging tools to differentiate active from latent and EPTB from pulmonary TB",
]:
e.append(b(item))
e.append(sp(10))
# 3.2 Peritoneal TB
e.append(p("<b>3.2 Peritoneal Tuberculosis (TB Peritonitis)</b>", sH2))
e.append(p("M. tuberculosis enters the peritoneal space from adjacent lymph nodes. Active pulmonary TB is <b>uncommon</b> in patients with TB peritonitis. Frequently coexists with cirrhosis, which may mask TB symptoms."))
e.append(sp(6))
e.append(p("<b>Clinical Features:</b>"))
for item in [
"Subacute abdominal pain, anorexia, abdominal swelling",
"Systemic symptoms: fever, night sweats, weight loss",
"Combination of fever + abdominal tenderness in a patient with ascites → ALWAYS perform paracentesis",
"Can mimic an acute abdomen",
"Intra-abdominal lymphadenopathy visible on CT scan",
]:
e.append(b(item))
e.append(sp(6))
e.append(p("<b>Ascitic Fluid Analysis in TB Peritonitis:</b>", sH3))
ascites_data = [
[Paragraph("<b>Parameter</b>", sLabel), Paragraph("<b>Finding</b>", sLabel), Paragraph("<b>Notes</b>", sLabel)],
["Character", "Exudative", "High protein, low SAAG (<1.1 g/dL) in non-cirrhotic"],
["WBC count", "50–10,000 cells/μL", "Predominantly lymphocytes; PMNs may predominate early"],
["AFB smear", "Rarely positive (~3%)", "Low yield; do not rely on smear alone"],
["AFB culture", "~50% positive", "Yield increases with large-volume submission"],
["SAAG", "<1.1 g/dL (non-cirrhotic)", "In cirrhotic patients SAAG may be >1.1 (cirrhosis dominates)"],
["ADA (ascitic fluid)", "≥39 IU/L = positive", "Sensitivity 93–100%, specificity 96–97% (optimal cut-off 39 IU/L)"],
["Laparoscopy", "Best test", "Characteristic visual findings + biopsy + histology + culture"],
]
tbl = drug_table(ascites_data[0], [
[Paragraph(str(r[i]), sBody) for i in range(3)] for r in ascites_data[1:]
], col_w=[3.5*cm, 5*cm, 8*cm])
e.append(tbl)
e.append(sp(6))
e.append(warn_box("In cirrhotic patients, ADA may be less sensitive for TB peritonitis (high SAAG dominates the ascitic picture). Laparoscopy with biopsy remains the gold standard."))
return e
# ── Section 4: TBM ───────────────────────────────────────────────────────────
def section4():
e = []
e.append(PageBreak())
e.append(section_header("4. TUBERCULOUS MENINGITIS (TBM)"))
e.append(sp(8))
e.append(p("TB of the CNS accounts for ~5% of EPTB in the USA. Peak incidence: neonates to 4-year-old children; also significant in adults (especially HIV-infected). If unrecognised, TBM is uniformly fatal."))
e.append(sp(8))
e.append(p("<b>4.1 Pathogenesis</b>", sH2))
e.append(p("TBM results from two sequential events:"))
for item in [
"<b>Step 1:</b> Haematogenous seeding of meninges and subpial regions → formation of subependymal tubercles",
"<b>Step 2:</b> Rupture of one or more tubercles → discharge of bacteria into the subarachnoid space",
"When complicating miliary TB, meningitis develops within weeks of infection",
"In children, TBM is an early post-primary event (usually within 6 months)",
]:
e.append(b(item))
e.append(sp(6))
e.append(p("<b>Pathological Findings:</b>", sH3))
for item in [
"Thick gelatinous exudate at base of brain — obliterates pontine and interpeduncular cisterns",
"Extends to floor of 3rd ventricle, optic chiasm, undersurfaces of temporal lobes",
"Microscopically: central caseation + epithelioid cells + Langhans giant cells + lymphocytes + plasma cells",
"Cranial nerve involvement as they traverse the subarachnoid space",
"Arteritis → brain infarction (basal ganglia lacunar infarcts; MCA → hemiparesis)",
"Blockage of basal cisterns → obstructive hydrocephalus",
"True meningoencephalitis — disease penetrates pia and ependyma into brain parenchyma",
]:
e.append(b(item))
e.append(sp(8))
e.append(p("<b>4.2 Clinical Features</b>", sH2))
e.append(p("<b>Key distinguishing feature:</b> symptoms evolve over <b>1–2 weeks</b> — much more insidious than acute bacterial meningitis."))
e.append(sp(4))
clin_data = [
[Paragraph("<b>Stage</b>", sLabel), Paragraph("<b>Features</b>", sLabel)],
["Prodrome\n(Days–weeks)", "Low-grade fever; malaise; headache (>50%); lethargy; confusion; stiff neck (75%); Kernig and Brudzinski signs"],
["Intermediate", "Cranial nerve palsies — ocular (most common), facial, deafness (20% at diagnosis); papilloedema; diplopia (up to 70%); hyponatraemia (SIADH is common)"],
["Late / Severe", "Focal neurologic deficits from haemorrhagic infarction; signs of raised ICP; coma; hydrocephalus"],
["Children/Infants", "Apathy, hyperirritability, vomiting, seizures; stiff neck may be absent"],
]
tbl = drug_table(clin_data[0], [
[Paragraph(str(r[0]), sBody), Paragraph(str(r[1]), sBody)] for r in clin_data[1:]
], col_w=[4*cm, 12.5*cm])
e.append(tbl)
e.append(sp(6))
e.append(warn_box("In ~2/3 of TBM patients, evidence of active TB is found elsewhere (usually lungs; occasionally small bowel, bone, kidney, or ear). Always obtain CXR and sputum AFB in suspected TBM."))
e.append(sp(8))
e.append(p("<b>4.3 CSF Findings in TBM</b>", sH2))
csf_data = [
[Paragraph("<b>Parameter</b>", sLabel), Paragraph("<b>Finding in TBM</b>", sLabel), Paragraph("<b>Notes</b>", sLabel)],
["Opening pressure", "Elevated", "Hydrocephalus common"],
["Appearance", "Clear / slightly turbid / xanthochromic", "May be turbid in severe disease"],
["WBC count", "Up to 1000–1500 cells/μL", "Lymphocyte predominance; PMNs may predominate early"],
["Protein", "Elevated: 1–8 g/L (100–800 mg/dL)", "Markedly elevated protein is typical"],
["Glucose", "Low (typically)", "CSF:serum glucose ratio <0.5; any of 3 parameters can be normal"],
["AFB smear", "Infrequently positive", "Repeated LPs increase yield; use large volume"],
["AFB culture", "Positive in up to 80%", "Gold standard; takes 4–8 weeks"],
["Xpert MTB/RIF", "Sensitivity up to 80%", "Preferred initial molecular test; negative does not exclude TB"],
["ADA", "Elevated", "Elevated in CSF; supports diagnosis"],
]
tbl = drug_table(csf_data[0], [
[Paragraph(str(r[i]), sBody) for i in range(3)] for r in csf_data[1:]
], col_w=[3.5*cm, 5.5*cm, 7.5*cm])
e.append(tbl)
e.append(sp(8))
e.append(p("<b>4.4 Neuroradiology — Classic Triad on CT/MRI</b>", sH2))
for item in [
"<b>Basal meningeal enhancement</b> (gadolinium-enhanced MRI/CT) — thick gelatinous exudate at base of brain",
"<b>Hydrocephalus</b> — obstructive, from blockage of basal cisterns",
"<b>Cerebral or brainstem infarction</b> — from arteritis of vessels traversing the exudate",
]:
e.append(b(item))
e.append(sp(4))
e.append(p("CT/MRI may also show rounded, contrast-enhancing ring lesions = tuberculomas."))
e.append(sp(8))
e.append(p("<b>4.5 Tuberculoma</b>", sH2))
e.append(p("Tumour-like masses of tuberculous granulation tissue (2–12 mm) in brain parenchyma. In the USA, rare; in developing countries they constitute 5–30% of all intracranial mass lesions."))
e.append(sp(4))
for item in [
"CT/MRI: contrast-enhanced ring lesions — biopsy required for definitive diagnosis",
"CSF: small lymphocytosis, elevated protein, <b>normal glucose</b> (serous meningitis pattern — key difference from TBM)",
"Responds to standard anti-TB chemotherapy; surgery if mass effect persists or no radiological response",
"Cerebellar tuberculomas are the most frequent intracranial tumours in children in some tropical countries",
]:
e.append(b(item))
e.append(sp(8))
e.append(p("<b>4.6 Prognosis</b>", sH2))
prog_data = [
[Paragraph("<b>Population</b>", sLabel), Paragraph("<b>Mortality</b>", sLabel), Paragraph("<b>Residual Sequelae</b>", sLabel)],
["Overall CNS TB", "~10%", "20–30% of survivors: cognitive deficits, seizures, cranial nerve palsies, hemiparesis"],
["HIV-infected patients", "~21%", "Higher due to diagnosis delays and drug resistance"],
["Diagnosed late (coma)", "~50%", "Near-complete mortality when treatment starts in coma"],
]
tbl = drug_table(prog_data[0], [
[Paragraph(str(r[i]), sBody) for i in range(3)] for r in prog_data[1:]
], col_w=[5*cm, 4*cm, 7.5*cm])
e.append(tbl)
return e
# ── Section 5: Diagnosis ─────────────────────────────────────────────────────
def section5():
e = []
e.append(PageBreak())
e.append(section_header("5. DIAGNOSIS SUMMARY"))
e.append(sp(8))
diag_data = [
[Paragraph("<b>Test</b>", sLabel),
Paragraph("<b>Pulmonary TB</b>", sLabel),
Paragraph("<b>Abdominal/Peritoneal TB</b>", sLabel),
Paragraph("<b>TB Meningitis</b>", sLabel)],
["TST / IGRA", "Positive (active); positive (latent)", "Positive", "Positive"],
["AFB smear", "+ve or –ve (sputum)", "Rarely +ve (ascitic fluid ~3%)", "Rarely +ve (CSF)"],
["AFB culture", "Gold standard; sputum", "~50% ascitic fluid; best from laparoscopic biopsy", "Up to 80% CSF; gold standard"],
["Xpert MTB/RIF", "Preferred initial test", "Equivocal for peritoneal TB", "Sensitivity up to 80%; preferred initial molecular test"],
["ADA", "Pleural fluid (high sensitivity)", "Ascitic fluid ≥39 IU/L (sens 100%, spec 97%)", "Elevated in CSF"],
["Imaging", "CXR: cavitation, upper lobe disease, miliary", "CT: LAD, omental thickening, ascites", "CT/MRI: basal enhancement, hydrocephalus, infarction"],
["Laparoscopy / LP", "Bronchoscopy BAL for difficult cases", "Best test for peritoneal TB (visual + biopsy)", "LP is cornerstone of diagnosis"],
["Histology", "BAL or biopsy: caseating granulomas", "Caseating granulomas on biopsy", "CSF: lymphocytosis, high protein, low glucose"],
]
tbl = drug_table(diag_data[0], [
[Paragraph(str(r[i]), sBody) for i in range(4)] for r in diag_data[1:]
], col_w=[3*cm, 4*cm, 5.5*cm, 4*cm])
e.append(tbl)
e.append(sp(8))
e.append(p("<b>Tuberculin Skin Test (TST) Cut-offs for Positivity:</b>", sH3))
tst_data = [
[Paragraph("<b>Induration</b>", sLabel), Paragraph("<b>Positive in:</b>", sLabel)],
["≥5 mm", "HIV-infected persons; recent TB contacts; fibrotic CXR changes; organ transplant or immunosuppressed patients"],
["≥10 mm", "High-risk medical conditions; healthcare workers; immigrants from high-incidence countries; children <4 years; residents of high-risk facilities"],
["≥15 mm", "Persons with no known risk factors for TB"],
]
tbl = drug_table(tst_data[0], [
[Paragraph(str(r[0]), sBody), Paragraph(str(r[1]), sBody)] for r in tst_data[1:]
], col_w=[3*cm, 13.5*cm])
e.append(tbl)
return e
# ── Section 6: Treatment DS-TB ───────────────────────────────────────────────
def section6():
e = []
e.append(PageBreak())
e.append(section_header("6. TREATMENT — DRUG-SUSCEPTIBLE TB (DS-TB)"))
e.append(sp(8))
e.append(p("<b>6.1 Standard 6-Month Regimen (Classic — Still Preferred for Certain Groups)</b>", sH2))
std_data = [
[Paragraph("<b>Phase</b>", sLabel), Paragraph("<b>Duration</b>", sLabel), Paragraph("<b>Drugs</b>", sLabel), Paragraph("<b>Notation</b>", sLabel)],
["Intensive", "2 months", "Isoniazid (H) + Rifampin (R) + Pyrazinamide (Z) + Ethambutol (E)", "2HRZE"],
["Continuation", "4 months", "Isoniazid (H) + Rifampin (R)", "4HR"],
]
tbl = drug_table(std_data[0], [
[Paragraph(str(r[i]), sBody) for i in range(4)] for r in std_data[1:]
], col_w=[2.5*cm, 2.5*cm, 9*cm, 2.5*cm])
e.append(tbl)
e.append(sp(4))
e.append(p("Global treatment success rate: <b>85%</b> (range 76–91%). Patients at higher risk of relapse: treat up to <b>9 months</b>."))
e.append(sp(8))
e.append(p("<b>6.2 NEW: 4-Month Regimen (2025 WHO + ATS/CDC/ERS/IDSA Update)</b>", sH2))
e.append(p("Based on the pivotal <b>TBTC Study 31/ACTG A5349</b> phase 3 RCT (Dorman et al., NEJM 2021), the 4-month regimen is now endorsed as non-inferior to 6 months for eligible patients."))
e.append(sp(6))
new_data = [
[Paragraph("<b>Phase</b>", sLabel), Paragraph("<b>Duration</b>", sLabel), Paragraph("<b>Drugs</b>", sLabel), Paragraph("<b>Notation</b>", sLabel)],
["Intensive", "2 months", "Isoniazid (H) + Rifapentine (P) + Moxifloxacin (M) + Pyrazinamide (Z)", "2HPMZ"],
["Continuation", "2 months", "Isoniazid (H) + Rifapentine (P) + Moxifloxacin (M)", "2HPM"],
]
tbl = drug_table(new_data[0], [
[Paragraph(str(r[i]), sBody) for i in range(4)] for r in new_data[1:]
], col_w=[2.5*cm, 2.5*cm, 9*cm, 2.5*cm])
e.append(tbl)
e.append(sp(6))
e.append(info_box("4-Month Regimen — Key Changes vs. Standard", [
"Ethambutol REPLACED by Moxifloxacin (fluoroquinolone)",
"Rifampin REPLACED by Rifapentine (longer half-life, more potent rifamycin)",
"Fixed doses: Rifapentine 1200 mg daily + Moxifloxacin 400 mg daily (7 days/week)",
"Total duration: 4 months (down from 6 months)",
], bg=HexColor("#EBF5EB"), border=C_GREEN))
e.append(sp(6))
e.append(p("<b>Trial Evidence (Study 31/A5349):</b>"))
for item in [
"Non-inferiority demonstrated in adults + adolescents ≥12 years",
"Cure rate: 84.5%; Retention on treatment: 99.7%; All-cause mortality: 0.4%",
"Grade 3+ adverse events: 18.8% (4-month) vs. 19.3% (6-month) — similar safety profiles",
]:
e.append(b(item))
e.append(sp(6))
eligible_tbl = Table([
[
Paragraph("<b>✓ Eligible for 4-month regimen</b>", make_style("_eg", fontSize=9, fontName="Helvetica-Bold", textColor=C_GREEN, leading=13)),
Paragraph("<b>✗ NOT eligible — use 6-month regimen</b>", make_style("_ieg", fontSize=9, fontName="Helvetica-Bold", textColor=C_RED, leading=13)),
],
[
"\n".join([f"• {x}" for x in [
"Adults and adolescents ≥12 years",
"Smear and culture-positive pulmonary DS-TB",
"PLHIV with CD4 ≥100 cells/mm³ (with caution)",
]]),
"\n".join([f"• {x}" for x in [
"PLHIV with CD4 <100 cells/mm³",
"Children <12 years",
"Pregnant, breastfeeding, postpartum women",
"Does not meet S31/A5349 eligibility criteria",
]]),
]
], colWidths=[(W - 4*cm)/2]*2)
eligible_tbl.setStyle(TableStyle([
("BACKGROUND", (0,0), (0,0), HexColor("#EBF5EB")),
("BACKGROUND", (1,0), (1,0), HexColor("#FDEDEC")),
("BACKGROUND", (0,1), (0,1), HexColor("#EBF5EB")),
("BACKGROUND", (1,1), (1,1), HexColor("#FDEDEC")),
("FONTSIZE", (0,0), (-1,-1), 8.5),
("LEADING", (0,0), (-1,-1), 13),
("TOPPADDING", (0,0), (-1,-1), 7),
("BOTTOMPADDING", (0,0), (-1,-1), 7),
("LEFTPADDING", (0,0), (-1,-1), 10),
("RIGHTPADDING", (0,0), (-1,-1), 10),
("GRID", (0,0), (-1,-1), 0.5, HexColor("#CCCCCC")),
("VALIGN", (0,0), (-1,-1), "TOP"),
]))
e.append(eligible_tbl)
e.append(sp(8))
e.append(p("<b>6.3 Drug Dosing and Side Effects</b>", sH2))
drug_data = [
[Paragraph("<b>Drug</b>", sLabel), Paragraph("<b>Adult Dose</b>", sLabel), Paragraph("<b>Children Dose</b>", sLabel), Paragraph("<b>Key Side Effects / Notes</b>", sLabel)],
["Isoniazid (H)", "5 mg/kg/day (single dose)", "10 mg/kg/day", "Peripheral neuropathy (give pyridoxine 50 mg/day prophylactically); hepatitis (esp. in alcoholics); lupus-like syndrome. Most effective single drug."],
["Rifampin (R)", "10 mg/kg/day", "15 mg/kg/day", "Hepatitis; orange discolouration of secretions (warn patients); potent P450 inducer (↓ many drugs including ART); flu-like syndrome"],
["Rifapentine (P)", "1200 mg/day (fixed)", "Weight-based", "Similar to rifampin; longer half-life; used in 4-month DS-TB regimen and weekly LTBI regimen (3HP)"],
["Pyrazinamide (Z)", "20–35 mg/kg/day", "Same range", "Hepatitis; hyperuricaemia (monitor uric acid); rash; GI upset; arthralgias"],
["Ethambutol (E)", "15 mg/kg/day", "15 mg/kg/day", "Optic neuropathy — monitor visual acuity + red-green colour discrimination regularly. Avoid doses >20 mg/kg. Risk ↑ in renal failure."],
["Moxifloxacin (M)", "400 mg/day", "Limited data", "QT prolongation (ECG monitoring required; avoid with other QT-prolonging drugs); tendinopathy; used in 4-month DS-TB and DR-TB regimens"],
["Pyridoxine (B6)", "50 mg/day (with INH)", "25 mg/day", "Prevents INH-induced peripheral neuropathy. Give to ALL patients on isoniazid (especially pregnant women, alcoholics, diabetics, elderly, malnourished)"],
["Ethionamide (Eto)", "15–25 mg/kg/day (divided, after meals)", "Same", "GI irritation (take after meals); optic neuropathy; hepatitis; metallic taste. Used in MDR-TB as 5th drug if needed."],
]
tbl = drug_table(drug_data[0], [
[Paragraph(str(r[i]), sBody) for i in range(4)] for r in drug_data[1:]
], col_w=[3*cm, 3*cm, 2.8*cm, 7.7*cm])
e.append(tbl)
e.append(sp(6))
e.append(warn_box("INH and rifampin can be given parenterally. All other first-line drugs are oral/NG tube only. Monitor LFTs monthly — discontinue INH if symptomatic hepatitis or LFTs >3–5× ULN."))
return e
# ── Section 7: DR-TB ─────────────────────────────────────────────────────────
def section7():
e = []
e.append(PageBreak())
e.append(section_header("7. DRUG-RESISTANT TB TREATMENT — WHO 2025 GUIDELINES"))
e.append(sp(8))
e.append(p("<b>7.1 Definitions</b>", sH2))
def_data = [
[Paragraph("<b>Classification</b>", sLabel), Paragraph("<b>Resistance Pattern</b>", sLabel)],
["Isoniazid-resistant TB (Hr-TB)", "Resistant to isoniazid; rifampicin-susceptible"],
["RR-TB", "Rifampicin-resistant (any pattern including monoresistance)"],
["MDR-TB", "Resistant to BOTH isoniazid + rifampicin"],
["Pre-XDR-TB", "MDR/RR-TB + resistance to any fluoroquinolone (levofloxacin or moxifloxacin)"],
["XDR-TB", "MDR/RR-TB + resistance to any fluoroquinolone + ≥1 of bedaquiline OR linezolid"],
]
tbl = drug_table(def_data[0], [
[Paragraph(str(r[0]), sBody), Paragraph(str(r[1]), sBody)] for r in def_data[1:]
], col_w=[5.5*cm, 11*cm])
e.append(tbl)
e.append(sp(8))
e.append(p("<b>7.2 Recommended Regimens — Three Tiers (WHO 2025)</b>", sH2))
e.append(p("<b>TIER 1 (Preferred): 6-Month All-Oral Regimens</b>", sH3))
e.append(p("For MDR/RR-TB with fluoroquinolone susceptibility — two options:"))
tier1_data = [
[Paragraph("<b>Regimen</b>", sLabel), Paragraph("<b>Drugs</b>", sLabel), Paragraph("<b>Duration</b>", sLabel), Paragraph("<b>Trial Basis</b>", sLabel)],
["BPaLM", "Bedaquiline + Pretomanid + Linezolid + Moxifloxacin", "6 months", "TB-PRACTECAL (Lancet, 2024); ZeNix trial"],
["BDLLfxC ★NEW 2025", "Bedaquiline + Delamanid + Linezolid + Levofloxacin + Clofazimine", "6 months", "BEAT-TB; endTB trials (2025 addition)"],
]
tbl = drug_table(tier1_data[0], [
[Paragraph(str(r[i]), sBody) for i in range(4)] for r in tier1_data[1:]
], col_w=[2.5*cm, 7.5*cm, 2.5*cm, 4*cm])
e.append(tbl)
e.append(sp(6))
e.append(p("For pre-XDR-TB (fluoroquinolone-resistant):"))
tier1b_data = [
[Paragraph("<b>Regimen</b>", sLabel), Paragraph("<b>Drugs</b>", sLabel), Paragraph("<b>Duration</b>", sLabel), Paragraph("<b>Notes</b>", sLabel)],
["BPaL", "Bedaquiline + Pretomanid + Linezolid", "6 months", "Preferred for pre-XDR-TB; ZeNix/TB-PRACTECAL"],
["BDLC", "Bedaquiline + Delamanid + Linezolid + Clofazimine", "6 months", "Alternative for pre-XDR-TB"],
]
tbl = drug_table(tier1b_data[0], [
[Paragraph(str(r[i]), sBody) for i in range(4)] for r in tier1b_data[1:]
], col_w=[2.5*cm, 7.5*cm, 2.5*cm, 4*cm])
e.append(tbl)
e.append(sp(6))
e.append(info_box("Extension to 9 months is permitted for:", [
"Severe or extensive disease (bilateral cavitary disease, extensive parenchymal involvement)",
"Immunosuppression (HIV with low CD4, organ transplant, immunosuppressive therapy)",
"Poor early response or slow culture conversion",
], bg=C_MINT, border=C_TEAL))
e.append(sp(6))
e.append(p("<b>TIER 2: 9-Month All-Oral Regimens</b>", sH3))
e.append(p("For MDR/RR-TB when <b>fluoroquinolone resistance is excluded</b>. As of end 2024, <b>99 countries</b> were using 9-month regimens (small decrease from 103 in 2023 as 6-month adoption accelerates)."))
e.append(sp(4))
tier2_data = [
[Paragraph("<b>Standard 9-month regimen (modified)</b>", sLabel), Paragraph("<b>Composition</b>", sLabel)],
["Modified all-oral 9-month", "Bedaquiline + Levofloxacin/Moxifloxacin + Ethionamide/Prothionamide + Pyrazinamide + Clofazimine + Isoniazid (high-dose) + Ethambutol\n(4–6 months intensive, 5 months continuation)"],
]
tbl = drug_table(tier2_data[0], [
[Paragraph(str(r[0]), sBody), Paragraph(str(r[1]), sBody)] for r in tier2_data[1:]
], col_w=[6*cm, 10.5*cm])
e.append(tbl)
e.append(sp(6))
e.append(p("<b>TIER 3 (Last Resort): 18–20 Month Individualized Regimens</b>", sH3))
for item in [
"For XDR-TB or patients with intolerance to bedaquiline and/or linezolid",
"Regimen designed by expert clinicians based on full drug susceptibility testing (DST)",
"May include injectable agent (amikacin) — last-resort injectable",
"Guided by WHO Group A/B/C drug classification",
"Treatment cost: USD 200–600 per person (vs. ~USD 50 for first-line DS-TB)",
]:
e.append(b(item))
e.append(sp(8))
e.append(p("<b>7.3 Drug Abbreviation Key</b>", sH2))
abbr_data = [
[Paragraph("<b>Letter</b>", sLabel), Paragraph("<b>Drug</b>", sLabel),
Paragraph("<b>Letter</b>", sLabel), Paragraph("<b>Drug</b>", sLabel)],
["B", "Bedaquiline", "Pa", "Pretomanid"],
["L", "Linezolid", "M", "Moxifloxacin"],
["D", "Delamanid", "Lfx", "Levofloxacin"],
["C", "Clofazimine", "H", "Isoniazid"],
["R", "Rifampicin", "Z", "Pyrazinamide"],
["E", "Ethambutol", "P", "Rifapentine"],
["S", "Streptomycin", "Am", "Amikacin"],
]
tbl = drug_table(abbr_data[0], [
[Paragraph(str(r[i]), sBody) for i in range(4)] for r in abbr_data[1:]
], col_w=[2*cm, 5.5*cm, 2*cm, 5.5*cm])
e.append(tbl)
e.append(sp(8))
e.append(p("<b>7.4 Key Trial Evidence for BPaLM</b>", sH2))
e.append(p("<b>TB-PRACTECAL (Lancet Respir Med, 2024) — PMID 37980911:</b>"))
e.append(p("Phase 2B-3 open-label RCT; 7 sites; Uzbekistan, Belarus, South Africa; n=552 enrolled; rifampicin-resistant pulmonary TB."))
trial_data = [
[Paragraph("<b>Outcome at 72 weeks</b>", sLabel), Paragraph("<b>BPaLM (n=138)</b>", sLabel), Paragraph("<b>Standard Care (n=137)</b>", sLabel), Paragraph("<b>p-value</b>", sLabel)],
["Unfavourable composite outcome", "12% (16/137)", "41% (56/137)", "<0.0001"],
["Grade 3+ or serious adverse events", "23%", "48%", "Sig. different"],
["Deaths by week 72", "0", "5", "—"],
["Early treatment discontinuation", "NNT = 8", "—", "—"],
]
tbl = drug_table(trial_data[0], [
[Paragraph(str(r[i]), sBody) for i in range(4)] for r in trial_data[1:]
], col_w=[6*cm, 3.5*cm, 3.5*cm, 3.5*cm])
e.append(tbl)
e.append(sp(6))
e.append(p("<b>BPaLM Systematic Review (Silva et al., J Bras Pneumol, 2025) — PMID 39813501:</b>"))
for item in [
"BPaLM reduced risk of unfavourable composite outcome vs. standard of care (NNT = 7)",
"Reduced early treatment discontinuation (NNT = 8)",
"Reduced serious adverse events (NNT = 5)",
"Conclusion: BPaLM is more effective AND safer than standard of care",
"Quality of evidence: Only 1 RCT met inclusion criteria — further evidence needed",
]:
e.append(b(item))
e.append(sp(6))
# Rapid adoption stats
adopt_data = [
[Paragraph("<b>Year</b>", sLabel), Paragraph("<b>Countries using 6-month MDR regimens</b>", sLabel), Paragraph("<b>People started on 6-month regimens</b>", sLabel)],
["2022", "41", "1,744"],
["2023", "60", "5,653"],
["2024", "97", "34,256 (~6× increase from 2023)"],
]
tbl = drug_table(adopt_data[0], [
[Paragraph(str(r[i]), sBody) for i in range(3)] for r in adopt_data[1:]
], col_w=[2.5*cm, 8*cm, 6*cm])
e.append(tbl)
e.append(sp(6))
e.append(p("Source: WHO Global TB Report 2025 (data as of 30 July 2025).", sSmall))
return e
# ── Section 8: Special Situations ───────────────────────────────────────────
def section8():
e = []
e.append(PageBreak())
e.append(section_header("8. TREATMENT — SPECIAL SITUATIONS"))
e.append(sp(8))
e.append(p("<b>8.1 TB Meningitis Treatment</b>", sH2))
tbm_trt_data = [
[Paragraph("<b>Phase</b>", sLabel), Paragraph("<b>Duration</b>", sLabel), Paragraph("<b>Drugs</b>", sLabel), Paragraph("<b>Notes</b>", sLabel)],
["Intensive", "2 months", "HRZE (Isoniazid + Rifampin + Pyrazinamide + Ethambutol)", "All 4 drugs; alternative: INH + PZA + high-dose RMP + Moxifloxacin"],
["Continuation", "7–10 months", "HR (Isoniazid + Rifampin)", "Total duration 9–12 months"],
]
tbl = drug_table(tbm_trt_data[0], [
[Paragraph(str(r[i]), sBody) for i in range(4)] for r in tbm_trt_data[1:]
], col_w=[2.5*cm, 2.5*cm, 7*cm, 4.5*cm])
e.append(tbl)
e.append(sp(6))
e.append(p("<b>CNS drug penetration (best to least):</b> INH > PZA > Rifampin > Ethambutol"))
e.append(sp(4))
e.append(p("<b>Adjunctive Dexamethasone (WHO-recommended for TBM):</b>"))
for item in [
"IV dexamethasone 0.4 mg/kg/day — taper by 0.1 mg/kg/week until week 4, then 0.1 mg/kg/day",
"Followed by oral dexamethasone 4 mg/day — taper by 1 mg/week over 4 weeks",
"Total: 6–8 week tapering course",
"Evidence: Reduces mortality from 41% to 32% (Thwaites et al., Vietnam RCT) — no reduction in neurological sequelae",
"CAUTION: A recent placebo-controlled trial showed <b>NO benefit in PLHIV</b> from adjunctive dexamethasone",
]:
e.append(b(item))
e.append(sp(4))
e.append(warn_box("Dexamethasone is recommended for non-HIV TBM. Avoid or use with caution in HIV-infected patients based on the most recent evidence. Always use only in conjunction with anti-TB drugs."))
e.append(sp(8))
e.append(p("<b>8.2 TB-HIV Coinfection</b>", sH2))
for item in [
"Start ART in <b>ALL</b> TB-HIV patients regardless of CD4 count (2025 WHO expansion)",
"Preferred DS-TB regimen when CD4 <100 cells/mm³: Standard 6-month 2HRZE/4HR (NOT the 4-month regimen)",
"TB-IRIS (Immune Reconstitution Inflammatory Syndrome): occurs in 5–50% starting ART; paradoxical worsening or unmasking of TB",
"CNS TB-IRIS is associated with high morbidity and mortality",
"Rifampin is a potent CYP3A4 inducer — reduce lopinavir/ritonavir dose or use efavirenz-based ART; consider rifabutin as a substitute",
"Drug-resistant TB in PLHIV: beware of pharmacokinetic interactions with bedaquiline + ART",
"Mortality from TBM in HIV ~21% vs. ~10% overall — largely from delayed diagnosis and drug resistance",
]:
e.append(b(item))
e.append(sp(8))
e.append(p("<b>8.3 TB in Pregnancy</b>", sH2))
for item in [
"<b>4-month rifapentine-moxifloxacin regimen is CONTRAINDICATED</b> — pregnant women were excluded from S31/A5349",
"Use standard 6-month 2HRZE/4HR",
"Avoid: Streptomycin (ototoxic to fetus), fluoroquinolones (teratogenicity concerns), ethionamide (teratogenic in animals)",
"Pyrazinamide: some guidelines avoid in 1st trimester; WHO recommends including it",
"Isoniazid: safe in pregnancy; increase pyridoxine to 25–50 mg/day",
"Rifampin: safe; may cause haemorrhagic disease of the newborn (give vitamin K at delivery)",
"Breastfeeding: compatible with all first-line drugs at standard doses",
"ART + anti-TB in pregnant HIV+ women: manage drug interactions carefully",
]:
e.append(b(item))
e.append(sp(8))
e.append(p("<b>8.4 Isoniazid-Resistant TB (Hr-TB)</b>", sH2))
for item in [
"Do NOT use INH-containing regimens — replace INH with a fluoroquinolone",
"WHO-recommended: Rifampicin + Pyrazinamide + Ethambutol + Levofloxacin for 6 months (RZELfx)",
"If fluoroquinolone resistance also present: individualized regimen",
"DST before starting therapy: essential for all new TB patients in high-burden settings",
]:
e.append(b(item))
e.append(sp(8))
e.append(p("<b>8.5 Latent TB Infection (LTBI) Treatment Options</b>", sH2))
ltbi_data = [
[Paragraph("<b>Regimen</b>", sLabel), Paragraph("<b>Drugs</b>", sLabel), Paragraph("<b>Duration</b>", sLabel), Paragraph("<b>Notes</b>", sLabel)],
["6H", "Isoniazid daily", "6 months", "Classic; widely used; 90% efficacy"],
["9H", "Isoniazid daily", "9 months", "Preferred by US CDC; highest efficacy"],
["3HP", "Isoniazid + Rifapentine once weekly", "3 months (12 doses)", "Directly observed; good completion rates"],
["4R", "Rifampin daily", "4 months", "Alternative; fewer hepatotoxic events than 9H"],
["3HR", "Isoniazid + Rifampin daily", "3 months", "Short course; used in some settings"],
]
tbl = drug_table(ltbi_data[0], [
[Paragraph(str(r[i]), sBody) for i in range(4)] for r in ltbi_data[1:]
], col_w=[2*cm, 4.5*cm, 3.5*cm, 6.5*cm])
e.append(tbl)
return e
# ── Section 9: Other EPTB ────────────────────────────────────────────────────
def section9():
e = []
e.append(PageBreak())
e.append(section_header("9. OTHER EXTRAPULMONARY MANIFESTATIONS"))
e.append(sp(8))
eptb_data = [
[Paragraph("<b>Site</b>", sLabel), Paragraph("<b>Key Features</b>", sLabel), Paragraph("<b>Specific Points</b>", sLabel)],
["Pott's Disease\n(Spinal TB)", "Lower thoracic + upper lumbar vertebrae most common; back or radicular pain", "Starts in subchondral anterior vertebral body; involves 2 adjacent vertebrae + disc; paravertebral 'cold abscess'; gibbus deformity; kyphosis; Pott's paraplegia from cord compression"],
["Pericardial TB", "Exudative pericardial effusion (serosanguineous); large volume (2–4 L possible)", "ADA: sensitivity 87%, specificity 89%; chronic constrictive pericarditis in 30–60%; 30–80% also have pulmonary TB"],
["Lymphadenitis", "Most common EPTB site; cervical nodes most often (scrofula)", "Painless, rubbery nodes; may soften, fluctuate, or form draining sinuses; biopsy + culture required"],
["Genitourinary TB", "Sterile pyuria on urinalysis (WBCs without bacteria)", "Dysuria, haematuria, flank pain; IVP/CT: calyceal distortion, strictures; urine AFB cultures ×3"],
["Miliary TB", "Haematogenous dissemination; acute systemic illness", "Diffuse 1–2 mm nodules on CXR; choroid tubercles on fundoscopy; meningitis in 20–30%; bone marrow biopsy may be positive"],
["Cutaneous TB", "Tuberculids: erythema nodosum, erythema induratum (subcutaneous nodules, lower extremities)", "Hypersensitivity reactions to mycobacterial antigens — NOT direct infection of skin; lupus vulgaris is direct skin infection"],
["Poncet's Disease", "Reactive arthritis + erythema nodosum", "Immune-mediated; treat underlying TB"],
]
tbl = drug_table(eptb_data[0], [
[Paragraph(str(r[i]), sBody) for i in range(3)] for r in eptb_data[1:]
], col_w=[3*cm, 5.5*cm, 8*cm])
e.append(tbl)
e.append(sp(8))
e.append(p("<b>TB-IRIS (Immune Reconstitution Inflammatory Syndrome)</b>", sH2))
for item in [
"Occurs in 5–50% of HIV patients starting antiretroviral therapy (ART) with concurrent TB",
"Exuberant inflammatory response against M. tuberculosis antigens as immune function recovers",
"Two forms: (1) paradoxical worsening (fever, lymphadenitis) in patients already on anti-TB; (2) unmasking of previously undiagnosed TB",
"CNS TB-IRIS: high morbidity and mortality — vigilance essential",
"Management: NSAIDs, corticosteroids (prednisone) in moderate-severe cases; continue ART and anti-TB",
]:
e.append(b(item))
return e
# ── Section 10: 2025 WHO Nutrition Guidelines ────────────────────────────────
def section10():
e = []
e.append(PageBreak())
e.append(section_header("10. WHO 2025 MODULE 6 — TB AND UNDERNUTRITION"))
e.append(sp(8))
e.append(p("Released <b>October 8, 2025</b>. Part of the WHO Consolidated Guidelines on Tuberculosis, Module 6: TB and Comorbidities. Undernutrition is one of the most significant drivers of the TB epidemic globally."))
e.append(sp(6))
e.append(info_box("New 2025 Recommendations (First-Time Formal Endorsement)", [
"Nutritional assessment and counselling for ALL people with TB AND their household contacts",
"Nutritional interventions for TB patients with undernutrition — regardless of age, drug resistance status, pregnancy status, or severity of undernutrition",
"Food assistance to PREVENT TB in household contacts of TB patients in food-insecure settings (based on RATIONS trial evidence)",
"Addresses nutrition as part of people-centred care under WHO's End TB Strategy",
], bg=C_MINT, border=C_TEAL))
e.append(sp(6))
e.append(p("These recommendations recognise that a significant proportion of people in households with TB may also be undernourished, and that addressing food insecurity can improve treatment outcomes and prevent new cases."))
e.append(sp(8))
# References
e.append(hr(C_NAVY, 1))
e.append(sp(6))
e.append(p("<b>KEY REFERENCES</b>", sH2))
refs = [
"Goldman-Cecil Medicine, 26th Edition — Chapter 299 (Pulmonary TB, Intestinal TB, Peritoneal TB, Osteoarticular TB)",
"Harrison's Principles of Internal Medicine, 22nd Edition (2025) — Extrapulmonary TB, TBM, CSF findings",
"Adams & Victor's Principles of Neurology, 12th Edition — Chapter 31 (Tuberculous Meningitis, Tuberculoma)",
"Murray & Nadel's Textbook of Respiratory Medicine — TB pleuritis, peritoneal TB, pericardial TB",
"Yamada's Textbook of Gastroenterology, 7th Edition — GI TB, peritoneal TB, ADA diagnostics",
"ROSEN's Emergency Medicine — CNS TB, GI disease, miliary TB",
"WHO Consolidated Guidelines on TB, Module 4: Treatment and Care (2025) — WHO.int",
"ATS/CDC/ERS/IDSA Clinical Practice Update (February 2025) — 4-month DS-TB regimen",
"TB-PRACTECAL Trial: Nyang'wa et al. Lancet Respir Med. 2024;12(2). PMID 37980911",
"BPaLM Systematic Review: Silva et al. J Bras Pneumol. 2025. PMID 39813501",
"RCT Linezolid doses in BPaL: Padmapriyadarsini et al. Clin Infect Dis. 2024. PMID 39194339",
"WHO Global TB Report 2025 (data as of 30 July 2025) — who.int",
"WHO Module 6: TB and Undernutrition (October 2025) — who.int",
]
for i, ref in enumerate(refs):
e.append(p(f"{i+1}. {ref}", make_style("_ref",
fontSize=8, textColor=HexColor("#444444"), fontName="Helvetica",
leading=12, spaceAfter=3, leftIndent=14, firstLineIndent=-14)))
e.append(sp(10))
e.append(hr(C_TEAL, 0.5))
e.append(p(f"This document was compiled for educational purposes. Always consult current national guidelines and specialist opinion for individual patient management. Compiled: {datetime.date.today().strftime('%B %Y')}.", sSmall))
return e
# ── Page number canvas ────────────────────────────────────────────────────────
def add_page_number(canvas, doc):
canvas.saveState()
# Header bar
canvas.setFillColor(C_NAVY)
canvas.rect(0, H - 1.5*cm, W, 1.5*cm, fill=1, stroke=0)
canvas.setFillColor(C_WHITE)
canvas.setFont("Helvetica-Bold", 9)
canvas.drawString(2*cm, H - 1.0*cm, "TUBERCULOSIS — Complete Clinical Reference")
canvas.setFont("Helvetica", 8)
canvas.drawRightString(W - 2*cm, H - 1.0*cm, datetime.date.today().strftime("%B %Y"))
# Footer
canvas.setFillColor(C_TEAL)
canvas.rect(0, 0, W, 1*cm, fill=1, stroke=0)
canvas.setFillColor(C_WHITE)
canvas.setFont("Helvetica", 8)
canvas.drawCentredString(W/2, 0.35*cm, f"Page {doc.page}")
canvas.restoreState()
# ── Main build ────────────────────────────────────────────────────────────────
def build_pdf(output_path):
doc = SimpleDocTemplate(
output_path,
pagesize=A4,
topMargin=2*cm,
bottomMargin=1.5*cm,
leftMargin=2*cm,
rightMargin=2*cm,
title="Tuberculosis — Complete Clinical Reference",
author="Orris AI Medical Reference",
subject="TB: Pulmonary, Abdominal, TBM, DR-TB, 2025 Guidelines",
)
story = []
story += cover_page()
story += toc_page()
story += section1()
story += section2()
story += section3()
story += section4()
story += section5()
story += section6()
story += section7()
story += section8()
story += section9()
story += section10()
doc.build(story, onFirstPage=add_page_number, onLaterPages=add_page_number)
print(f"PDF saved: {output_path}")
if __name__ == "__main__":
build_pdf("/home/daytona/workspace/tb-pdf/Tuberculosis_Complete_Reference.pdf")