~/copd-review/generate_pdf.py
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, PageBreak, KeepTogether
)
from reportlab.lib.enums import TA_LEFT, TA_CENTER, TA_JUSTIFY
from reportlab.platypus import Image as RLImage
import urllib.request
import os
OUTPUT = "/home/daytona/workspace/copd-review/COPD_Comprehensive_Review.pdf"
os.makedirs("/home/daytona/workspace/copd-review", exist_ok=True)
# ── Styles ──────────────────────────────────────────────────────────────────
styles = getSampleStyleSheet()
title_style = ParagraphStyle(
"Title", parent=styles["Title"],
fontSize=22, textColor=colors.HexColor("#1a3a5c"),
spaceAfter=6, alignment=TA_CENTER, fontName="Helvetica-Bold"
)
subtitle_style = ParagraphStyle(
"Subtitle", parent=styles["Normal"],
fontSize=11, textColor=colors.HexColor("#4a7090"),
spaceAfter=14, alignment=TA_CENTER, fontName="Helvetica-Oblique"
)
h1_style = ParagraphStyle(
"H1", parent=styles["Heading1"],
fontSize=14, textColor=colors.white,
backColor=colors.HexColor("#1a3a5c"),
spaceBefore=14, spaceAfter=6,
leftIndent=-12, rightIndent=-12,
borderPad=6, fontName="Helvetica-Bold"
)
h2_style = ParagraphStyle(
"H2", parent=styles["Heading2"],
fontSize=12, textColor=colors.HexColor("#1a3a5c"),
spaceBefore=10, spaceAfter=4, fontName="Helvetica-Bold",
borderWidth=0, borderPadding=0,
underlineWidth=1
)
h3_style = ParagraphStyle(
"H3", parent=styles["Heading3"],
fontSize=10.5, textColor=colors.HexColor("#2c5f80"),
spaceBefore=8, spaceAfter=3, fontName="Helvetica-Bold"
)
body_style = ParagraphStyle(
"Body", parent=styles["Normal"],
fontSize=9.5, leading=14, alignment=TA_JUSTIFY,
spaceAfter=5, fontName="Helvetica"
)
bullet_style = ParagraphStyle(
"Bullet", parent=body_style,
leftIndent=18, bulletIndent=6, spaceAfter=3
)
note_style = ParagraphStyle(
"Note", parent=body_style,
fontSize=8.5, textColor=colors.HexColor("#555555"),
backColor=colors.HexColor("#f0f4f8"),
leftIndent=10, rightIndent=10,
borderPad=5, spaceAfter=6, spaceBefore=4,
fontName="Helvetica-Oblique"
)
caption_style = ParagraphStyle(
"Caption", parent=styles["Normal"],
fontSize=8, textColor=colors.HexColor("#555555"),
alignment=TA_CENTER, fontName="Helvetica-Oblique",
spaceAfter=8
)
source_style = ParagraphStyle(
"Source", parent=styles["Normal"],
fontSize=7.5, textColor=colors.HexColor("#777777"),
spaceAfter=2, fontName="Helvetica-Oblique"
)
# ── Table helpers ─────────────────────────────────────────────────────────
H_BLUE = colors.HexColor("#1a3a5c")
H_LIGHT = colors.HexColor("#d6e4f0")
ROW_ALT = colors.HexColor("#f4f8fb")
def make_table(data, col_widths=None, header_rows=1):
t = Table(data, colWidths=col_widths, repeatRows=header_rows)
style = [
("BACKGROUND", (0, 0), (-1, header_rows - 1), H_BLUE),
("TEXTCOLOR", (0, 0), (-1, header_rows - 1), colors.white),
("FONTNAME", (0, 0), (-1, header_rows - 1), "Helvetica-Bold"),
("FONTSIZE", (0, 0), (-1, header_rows - 1), 9),
("FONTSIZE", (0, header_rows), (-1, -1), 8.5),
("FONTNAME", (0, header_rows), (-1, -1), "Helvetica"),
("ROWBACKGROUNDS", (0, header_rows), (-1, -1), [colors.white, ROW_ALT]),
("GRID", (0, 0), (-1, -1), 0.4, colors.HexColor("#aaccdd")),
("VALIGN", (0, 0), (-1, -1), "TOP"),
("LEFTPADDING", (0, 0), (-1, -1), 6),
("RIGHTPADDING", (0, 0), (-1, -1), 6),
("TOPPADDING", (0, 0), (-1, -1), 4),
("BOTTOMPADDING",(0, 0), (-1, -1), 4),
]
t.setStyle(TableStyle(style))
return t
def p(text, style=None):
return Paragraph(text, style or body_style)
def h1(text): return Paragraph(text, h1_style)
def h2(text): return Paragraph(text, h2_style)
def h3(text): return Paragraph(text, h3_style)
def sp(n=6): return Spacer(1, n)
def hr(): return HRFlowable(width="100%", thickness=0.5, color=colors.HexColor("#aaccdd"), spaceAfter=4)
# ── Download diagram image ─────────────────────────────────────────────────
IMG_URL = "https://cdn.orris.care/cdss_images/e6ff325425e9bfcfa91d49f229e35786e277d6ded00f56f29fa0a379eceb5ce1.png"
IMG_PATH = "/home/daytona/workspace/copd-review/emphysema_types.png"
try:
urllib.request.urlretrieve(IMG_URL, IMG_PATH)
img_ok = True
except Exception as e:
print(f"Image download failed: {e}")
img_ok = False
# ── Build content ─────────────────────────────────────────────────────────
story = []
# ── Cover ──────────────────────────────────────────────────────────────────
story.append(sp(40))
story.append(Paragraph("Chronic Obstructive Pulmonary Disease", title_style))
story.append(Paragraph("COPD", ParagraphStyle("bigtitle", parent=styles["Normal"],
fontSize=48, textColor=colors.HexColor("#d6e4f0"), alignment=TA_CENTER,
fontName="Helvetica-Bold", spaceAfter=4)))
story.append(Paragraph("Comprehensive Clinical Review", subtitle_style))
story.append(sp(8))
story.append(HRFlowable(width="60%", thickness=2, color=H_BLUE, hAlign="CENTER", spaceAfter=10))
story.append(Paragraph(
"Sources: Robbins Pathology · Fishman's Pulmonary Diseases · Murray & Nadel's Respiratory Medicine<br/>"
"Katzung's Pharmacology · Washington Manual · Rosen's Emergency Medicine · GOLD Guidelines",
subtitle_style))
story.append(sp(6))
story.append(Paragraph("April 2026", subtitle_style))
story.append(PageBreak())
# ══════════════════════════════════════════════════════════════════
# 1. DEFINITION
# ══════════════════════════════════════════════════════════════════
story.append(h1("1. Definition"))
story.append(p(
"COPD is a progressive, largely <b>irreversible airflow limitation</b> caused by an abnormal "
"inflammatory response of the lung to noxious particles or gases — most commonly cigarette smoke. "
"It encompasses two main pathologic entities that frequently coexist:"
))
story.append(p("• <b>Emphysema</b> — destruction of alveolar walls with permanent airspace enlargement distal to the terminal bronchiole.", bullet_style))
story.append(p("• <b>Chronic bronchitis</b> — clinically defined as productive cough for ≥3 consecutive months "
"in ≥2 consecutive years, in the absence of another identifiable cause.", bullet_style))
story.append(sp())
story.append(p(
"COPD is the <b>third most common cause of death</b> in the United States and accounts for "
">$40 billion per year in direct and indirect healthcare costs."
))
story.append(p("<i>— Katzung's Basic and Clinical Pharmacology, 16th ed.</i>", source_style))
story.append(sp(10))
# ══════════════════════════════════════════════════════════════════
# 2. RISK FACTORS
# ══════════════════════════════════════════════════════════════════
story.append(h1("2. Risk Factors"))
story.append(sp(4))
rf_data = [
["Risk Factor", "Notes"],
["Cigarette smoking", "Primary cause (~90% of cases). Radiographic changes occur even in smokers with normal spirometry."],
["α₁-Antitrypsin (AAT) deficiency", "Genetic cause — panacinar emphysema, typically lower-lobe, often in non-smokers or young patients."],
["Air pollution", "Occupational dusts (grain, cotton, silica), SO₂, NO₂, ozone, biomass fuels."],
["HIV/AIDS", "Associated with premature emphysema and accelerated lung function decline."],
["Recurrent respiratory infections", "Do not initiate COPD but sustain and worsen established disease."],
]
story.append(make_table(rf_data, col_widths=[5*cm, 11.5*cm]))
story.append(sp(10))
# ══════════════════════════════════════════════════════════════════
# 3. PATHOPHYSIOLOGY
# ══════════════════════════════════════════════════════════════════
story.append(h1("3. Pathophysiology"))
story.append(h2("3a. Emphysema — Protease-Antiprotease Imbalance"))
story.append(p(
"Cigarette smoke recruits neutrophils and macrophages → release of proteases (elastase, MMPs) "
"→ destruction of elastic tissue in alveolar walls. Normally α₁-antitrypsin (AAT) neutralises "
"these proteases; AAT deficiency or overwhelming protease burden allows unchecked destruction."
))
story.append(p(
"<b>Consequences:</b> Loss of alveolar elastic recoil → airspace enlargement → loss of radial "
"airway traction → expiratory airflow obstruction → air trapping and <b>dynamic hyperinflation</b>. "
"During exercise, end-expiratory lung volume (EELV) fails to decline, causing inspiratory reserve "
"loss and severe dyspnea."
))
story.append(p("<i>— Fishman's Pulmonary Diseases & Disorders</i>", source_style))
story.append(sp(6))
story.append(h2("3b. Chronic Bronchitis — Mucus Dysfunction & Airway Inflammation"))
story.append(p("<b>Mucus hypersecretion:</b> submucosal gland hyperplasia + goblet cell metaplasia "
"driven by IL-13, histamine, and acquired CFTR dysfunction from smoking.", bullet_style))
story.append(p("<b>Airway inflammation:</b> neutrophils, macrophages, lymphocytes → chronic "
"bronchiolitis → small airway fibrosis.", bullet_style))
story.append(p("<b>Impaired mucociliary clearance:</b> smoking damages cilia; inhibits CFTR / "
"activates ENaC → mucus hyperconcentration → persistent infection (esp. H. influenzae).", bullet_style))
story.append(sp(4))
story.append(Paragraph(
"⚑ The extent of small-airway mucus occlusion correlates with degree of airflow obstruction "
"and predicts longevity — even in patients with an emphysematous phenotype.",
note_style
))
story.append(p("<i>— Fishman's Pulmonary Diseases & Disorders</i>", source_style))
story.append(sp(10))
# ══════════════════════════════════════════════════════════════════
# 4. TYPES OF EMPHYSEMA
# ══════════════════════════════════════════════════════════════════
story.append(h1("4. Types of Emphysema"))
story.append(sp(4))
if img_ok:
img = RLImage(IMG_PATH, width=9*cm, height=9*cm)
img_table = Table([[img]], colWidths=[16.5*cm])
img_table.setStyle(TableStyle([
("ALIGN", (0,0), (-1,-1), "CENTER"),
("VALIGN", (0,0), (-1,-1), "MIDDLE"),
]))
story.append(img_table)
story.append(Paragraph(
"Fig. Clinically significant patterns of emphysema. (A) Normal acinus. "
"(B) Centriacinar emphysema — dilation of respiratory bronchioles. "
"(C) Panacinar emphysema — uniform distension of entire acinus.<br/>"
"<i>Source: Robbins, Cotran & Kumar — Pathologic Basis of Disease</i>",
caption_style
))
story.append(sp(4))
emp_data = [
["Type", "Distribution", "Association", "Key Features"],
["Centriacinar\n(Centrilobular)",
"Respiratory bronchioles (central acinus); upper lobes",
"Cigarette smoking",
">95% of clinically significant emphysema; both normal and emphysematous spaces in same acinus"],
["Panacinar\n(Panlobular)",
"Entire acinus uniformly; lower lobes / bases",
"α₁-AT deficiency (± smoking)",
"Most severe at lung bases; exacerbated by smoking"],
["Paraseptal\n(Distal acinar)",
"Distal acinus; subpleural",
"Spontaneous pneumothorax in young adults",
"Proximal acinus spared; blebs / bullae near pleura"],
["Irregular",
"Variable",
"Scar tissue (e.g., healed TB)",
"Usually clinically silent"],
]
story.append(make_table(emp_data, col_widths=[3.5*cm, 4*cm, 4.5*cm, 4.5*cm]))
story.append(sp(10))
# ══════════════════════════════════════════════════════════════════
# 5. CLINICAL FEATURES
# ══════════════════════════════════════════════════════════════════
story.append(h1("5. Clinical Features — Pink Puffer vs Blue Bloater"))
story.append(sp(4))
cf_data = [
["Feature", "Emphysema Dominant\n(\"Pink Puffer\")", "Chronic Bronchitis Dominant\n(\"Blue Bloater\")"],
["Age (years)", "50–75", "40–45"],
["Dyspnea", "Severe, early", "Mild, late"],
["Cough / Sputum", "Late; scanty sputum", "Early; copious sputum"],
["Cyanosis", "Absent (well-oxygenated at rest)", "Present"],
["Cor pulmonale", "Uncommon until end-stage", "Common"],
["Infections", "Occasional", "Common"],
["Elastic recoil", "Markedly reduced", "Normal"],
["Chest X-ray", "Hyperinflation; normal heart size", "Prominent vessels; large heart"],
["Blood gases", "Relatively normal at rest", "Hypoxemia + hypercapnia"],
["Appearance", "Barrel-chested, pursed-lip breathing", "Obese, cyanotic, oedematous"],
]
story.append(make_table(cf_data, col_widths=[4.5*cm, 6*cm, 6*cm]))
story.append(sp(4))
story.append(p("Most patients fall between these two extremes."))
story.append(p("<i>— Robbins, Cotran & Kumar — Pathologic Basis of Disease</i>", source_style))
story.append(sp(10))
# ══════════════════════════════════════════════════════════════════
# 6. DIAGNOSIS
# ══════════════════════════════════════════════════════════════════
story.append(h1("6. Diagnosis"))
story.append(h2("Spirometry (Gold Standard)"))
story.append(p("• <b>FEV₁/FVC < 0.70</b> post-bronchodilator = confirms obstructive pattern (not fully reversible, unlike asthma).", bullet_style))
story.append(p("• FEV₁ % predicted classifies severity (GOLD staging).", bullet_style))
story.append(p("• Elevated TLC (hyperinflation) and elevated RV (air trapping) on lung volumes.", bullet_style))
story.append(p("• <b>DLCO</b>: reduced in emphysema; independent predictor of mortality.", bullet_style))
story.append(h2("Physical Examination"))
story.append(p(
"Signs of hyperinflation (low diaphragm, hyperresonance, decreased breath sounds) are highly "
"specific but only apparent in advanced disease. A thyroid-to-sternal notch distance <b>< 4 cm</b> "
"in a smoker > 45 years is highly indicative of COPD."
))
story.append(Paragraph(
"⚑ Finger clubbing is rare in COPD. If present, consider bronchiectasis, asbestosis, or lung cancer.",
note_style
))
story.append(sp(4))
story.append(h2("Imaging"))
story.append(p("• <b>Chest X-ray</b>: flat diaphragm, hyperlucency, increased AP diameter.", bullet_style))
story.append(p("• <b>HRCT</b>: gold standard for emphysema quantification; identifies small airway disease and air trapping.", bullet_style))
story.append(h2("When to Screen for α₁-Antitrypsin Deficiency"))
story.append(sp(4))
aat_data = [
["Indications for AAT Testing"],
["Emphysema onset < 45 years"],
["Emphysema in a non-smoker"],
["Lower-lobe predominant (panacinar) emphysema"],
["Family history of early-onset emphysema or cirrhosis"],
["Bronchiectasis without identifiable cause"],
["c-ANCA positive vasculitis (e.g., GPA)"],
["Necrotizing panniculitis"],
]
story.append(make_table(aat_data, col_widths=[16.5*cm]))
story.append(sp(10))
# ══════════════════════════════════════════════════════════════════
# 7. GOLD CLASSIFICATION
# ══════════════════════════════════════════════════════════════════
story.append(h1("7. GOLD Classification"))
story.append(h2("Airflow Limitation Severity (post-bronchodilator; all require FEV₁/FVC < 0.70)"))
story.append(sp(4))
gold_data = [
["GOLD Grade", "Severity", "FEV₁ % Predicted"],
["GOLD 1", "Mild", "≥ 80%"],
["GOLD 2", "Moderate", "50–79%"],
["GOLD 3", "Severe", "30–49%"],
["GOLD 4", "Very Severe", "< 30%"],
]
story.append(make_table(gold_data, col_widths=[4*cm, 6.25*cm, 6.25*cm]))
story.append(sp(8))
story.append(h2("GOLD ABCD Assessment Tool"))
story.append(p(
"Beyond spirometry, patients are further categorised by <b>symptom burden</b> "
"(mMRC dyspnea scale or CAT score) and <b>exacerbation history</b> to guide therapy:"
))
story.append(sp(4))
abcd_data = [
["Group", "Symptoms", "Exacerbation History"],
["A", "Few (mMRC 0–1 / CAT < 10)", "0–1 (not hospitalized)"],
["B", "Many (mMRC ≥2 / CAT ≥10)", "0–1 (not hospitalized)"],
["C", "Few", "≥2, or ≥1 leading to hospitalization"],
["D", "Many", "≥2, or ≥1 leading to hospitalization"],
]
story.append(make_table(abcd_data, col_widths=[2.5*cm, 7*cm, 7*cm]))
story.append(p("<i>Source: Rosen's Emergency Medicine; GOLD 2020 Guidelines</i>", source_style))
story.append(sp(10))
# ══════════════════════════════════════════════════════════════════
# 8. MANAGEMENT — STABLE COPD
# ══════════════════════════════════════════════════════════════════
story.append(h1("8. Management of Stable COPD"))
story.append(p(
"Goals: prevent progression, relieve symptoms, improve exercise capacity and quality of life, "
"prevent and treat exacerbations, and improve survival. <i>— Fishman's</i>"
))
story.append(h2("Non-Pharmacological Interventions"))
story.append(sp(4))
nonpharm_data = [
["Intervention", "Notes"],
["Smoking cessation", "Single most effective intervention to slow FEV₁ decline; indicated at any COPD stage."],
["Pulmonary rehabilitation", "Improves exercise tolerance, dyspnea, and depression; reduces exacerbation frequency and hospitalizations."],
["Vaccinations", "Influenza (annual) and pneumococcal vaccines reduce exacerbations and mortality."],
["Long-term oxygen therapy", "Indicated when PaO₂ ≤55 mmHg (or ≤59 mmHg with cor pulmonale / polycythemia); shown to improve survival."],
["NIV / BiPAP", "For COPD–OSA overlap syndrome; consider nocturnal NIV in chronic hypercapnia (pCO₂ persistently elevated)."],
["Surgical options", "Lung volume reduction surgery (LVRS) for selected emphysema; lung transplantation for end-stage; bullectomy where indicated."],
]
story.append(make_table(nonpharm_data, col_widths=[5*cm, 11.5*cm]))
story.append(sp(8))
story.append(h2("Pharmacotherapy — Stable COPD"))
story.append(h3("Short-Acting Agents (Rescue)"))
story.append(p("• <b>SABA</b> (salbutamol/albuterol): first-line for acute symptom relief.", bullet_style))
story.append(p("• <b>SAMA</b> (ipratropium): anticholinergic; can be combined with SABA for additive bronchodilation.", bullet_style))
story.append(h3("Long-Acting Agents (Maintenance)"))
story.append(p("• <b>LABA</b> (salmeterol, formoterol, indacaterol): reduces dyspnea and exacerbation frequency.", bullet_style))
story.append(p("• <b>LAMA</b> (tiotropium, umeclidinium): often preferred as first maintenance agent; can combine LABA + LAMA.", bullet_style))
story.append(h3("Inhaled Corticosteroids (ICS)"))
story.append(p(
"Less central than in asthma; associated with increased risk of bacterial pneumonia. "
"Recommended for GOLD 3–4, frequent exacerbations, or when blood eosinophil counts suggest benefit. "
"Current GOLD guidelines use <b>blood eosinophil levels</b> to guide ICS use."
))
story.append(h3("Roflumilast (PDE4 Inhibitor)"))
story.append(p(
"Oral selective phosphodiesterase-4 inhibitor. Improves pulmonary function and <b>reduces "
"exacerbation frequency</b>. Particularly indicated for chronic bronchitis phenotype with "
"frequent exacerbations. <i>— Katzung's</i>"
))
story.append(h3("Theophylline"))
story.append(p(
"A recent large placebo-controlled RCT of low-dose theophylline showed <b>no benefit</b> on "
"exacerbation frequency. No longer recommended as standard therapy. <i>— Katzung's</i>"
))
story.append(sp(10))
# ══════════════════════════════════════════════════════════════════
# 9. EXACERBATIONS
# ══════════════════════════════════════════════════════════════════
story.append(h1("9. COPD Exacerbations"))
story.append(h2("Definition"))
story.append(p(
"Increased dyspnea, often with increased cough, sputum production/purulence, wheezing, or "
"chest tightness — in the absence of an alternative explanation. <i>— Washington Manual</i>"
))
story.append(h2("Causes & Differential"))
story.append(p("• <b>Viral</b>: rhinovirus (most common), influenza, parainfluenza, coronavirus.", bullet_style))
story.append(p("• <b>Bacterial</b>: H. influenzae, S. pneumoniae, Moraxella catarrhalis; "
"gram-negatives incl. Pseudomonas in high-risk patients.", bullet_style))
story.append(p("• <b>Environmental</b>: air pollution, ozone, nitrogen dioxide.", bullet_style))
story.append(p("• <b>Differential</b>: pneumothorax, pneumonia, pleural effusion, CHF, PE, ACS.", bullet_style))
story.append(sp(6))
story.append(h2("Severity & Admission Criteria"))
story.append(sp(4))
sev_data = [
["Severity", "Definition", "Setting"],
["Mild", "Worsening symptoms only", "Home — increase inhaler frequency"],
["Moderate", "Requires antibiotics ± systemic corticosteroids", "Outpatient or ED"],
["Severe", "Requires ED visit or hospitalisation", "Hospital"],
]
story.append(make_table(sev_data, col_widths=[3*cm, 7*cm, 6.5*cm]))
story.append(sp(6))
story.append(Paragraph(
"⚑ ICU indications: need for mechanical ventilation · hemodynamic instability · "
"severe dyspnea not responding to therapy · altered mental status · "
"persistent hypoxemia / hypercapnia / respiratory acidosis despite O₂ and NIV.",
note_style
))
story.append(sp(6))
story.append(h2("Treatment of Exacerbations"))
story.append(h3("Bronchodilators"))
story.append(p("• SABAs (albuterol 2.5 mg nebulized q1–4h) — first-line.", bullet_style))
story.append(p("• Add ipratropium 0.5 mg q4h if inadequate response.", bullet_style))
story.append(h3("Systemic Corticosteroids"))
story.append(p("• Prednisone <b>40 mg/day × 5 days</b> (outpatient); 30–60 mg/day × 5–10 days (inpatient).", bullet_style))
story.append(p("• Shortens symptom duration and reduces treatment failure.", bullet_style))
story.append(h3("Antibiotics"))
story.append(sp(4))
abx_data = [
["Patient Risk", "Pathogens", "Antibiotic (one of)"],
["No risk factors",
"H. influenzae\nS. pneumoniae\nM. catarrhalis",
"Macrolide · 2nd/3rd-gen cephalosporin · Doxycycline · TMP-SMX\n(3–7 days)"],
["Risk factors present*",
"Above + gram-negatives incl. Pseudomonas",
"Antipseudomonal fluoroquinolone (levofloxacin 500–750 mg QD)\nor antipseudomonal β-lactam"],
]
story.append(make_table(abx_data, col_widths=[4*cm, 5.5*cm, 7*cm]))
story.append(p(
"* Risk factors: age >65, FEV₁ <50%, >3 exacerbations/year, "
"cardiac comorbidity, antibiotic use within past 3 months.",
ParagraphStyle("small", parent=source_style, fontSize=8)
))
story.append(sp(6))
story.append(h3("Oxygen & NIV"))
story.append(p("• Controlled O₂ at <b>lowest flow</b> needed to reverse hypoxemia (target SpO₂ 88–92%) — avoid worsening hypercapnia.", bullet_style))
story.append(p("• <b>NIV/BiPAP</b>: preferred over invasive ventilation in hypercapnic respiratory failure — reduces intubation rate and mortality.", bullet_style))
story.append(sp(10))
# ══════════════════════════════════════════════════════════════════
# 10. COMPLICATIONS
# ══════════════════════════════════════════════════════════════════
story.append(h1("10. Complications"))
story.append(sp(4))
comp_data = [
["Complication", "Notes"],
["Pulmonary hypertension / Cor pulmonale",
"COPD is the most common cause of CLD-related PH (>80% of cases). Severe PH (mPAP >35–40 mmHg) is uncommon. — Murray & Nadel's"],
["Respiratory failure", "Type I (hypoxemic) or Type II (hypercapnic); hypercapnia from alveolar hypoventilation + V/Q mismatch."],
["Secondary polycythemia", "Driven by chronic hypoxemia → ↑ EPO → erythrocytosis."],
["Pneumothorax", "Rupture of subpleural blebs (especially in paraseptal emphysema); may be fatal."],
["Lung cancer", "Markedly increased risk — shared risk factor (smoking), but COPD is an independent risk factor."],
["COPD–OSA overlap syndrome", "Severe pulmonary hypertension and daytime hypercapnia; treat with CPAP."],
["Depression & anxiety", "Common; associated with poorer prognosis, more exacerbations, and poor health status."],
["Mucus plugging", "Widespread small-airway occlusion is an independent predictor of increased all-cause mortality."],
]
story.append(make_table(comp_data, col_widths=[5.5*cm, 11*cm]))
story.append(sp(10))
# ══════════════════════════════════════════════════════════════════
# 11. PROGNOSIS
# ══════════════════════════════════════════════════════════════════
story.append(h1("11. Prognosis"))
story.append(p(
"COPD is progressive; no currently proven treatment reverses the underlying disease. "
"The <b>BODE index</b> (BMI, airflow Obstruction, Dyspnea, Exercise capacity) predicts mortality "
"better than FEV₁ alone. Long-standing disease, particularly with a bronchitic component, commonly "
"leads to pulmonary hypertension, cor pulmonale, and death from heart failure. Death may also result "
"from acute respiratory failure due to infections superimposed on COPD, or from fatal pneumothorax "
"following bleb rupture."
))
story.append(sp(4))
story.append(Paragraph(
"⚑ Smoking cessation remains the <b>only intervention proven to slow the excessive decline "
"in FEV₁</b> — and is beneficial even in patients with advanced disease.",
note_style
))
story.append(sp(16))
# ── Sources ──────────────────────────────────────────────────────────────────
story.append(hr())
story.append(h2("Sources"))
sources = [
"Robbins, Cotran & Kumar — Pathologic Basis of Disease",
"Robbins & Kumar Basic Pathology (Robbins Pathology)",
"Fishman's Pulmonary Diseases and Disorders, 2-Volume Set",
"Murray & Nadel's Textbook of Respiratory Medicine, 2-Volume",
"Katzung's Basic and Clinical Pharmacology, 16th Edition",
"The Washington Manual of Medical Therapeutics",
"Rosen's Emergency Medicine — Concepts and Clinical Practice",
"Global Initiative for Chronic Obstructive Lung Disease (GOLD) 2020/2021 Guidelines",
]
for s in sources:
story.append(p(f"• {s}", source_style))
# ── Build PDF ─────────────────────────────────────────────────────────────
doc = SimpleDocTemplate(
OUTPUT,
pagesize=A4,
leftMargin=2*cm, rightMargin=2*cm,
topMargin=2.2*cm, bottomMargin=2.2*cm,
title="COPD Comprehensive Review",
author="Orris Medical AI",
subject="Chronic Obstructive Pulmonary Disease"
)
def on_page(canvas, doc):
canvas.saveState()
w, h = A4
# Header bar
canvas.setFillColor(H_BLUE)
canvas.rect(0, h - 1.5*cm, w, 1.5*cm, fill=1, stroke=0)
canvas.setFillColor(colors.white)
canvas.setFont("Helvetica-Bold", 9)
canvas.drawString(2*cm, h - 0.95*cm, "COPD — Comprehensive Clinical Review")
canvas.setFont("Helvetica", 8)
canvas.drawRightString(w - 2*cm, h - 0.95*cm, "April 2026")
# Footer
canvas.setFillColor(colors.HexColor("#777777"))
canvas.setFont("Helvetica", 7.5)
canvas.drawCentredString(w/2, 1*cm, f"Page {doc.page}")
canvas.restoreState()
doc.build(story, onFirstPage=on_page, onLaterPages=on_page)
print(f"PDF created: {OUTPUT}")