Make a pdf on the topic dry eye - defination , classification, evaluation and step wise management - take reference from the standard dews

Reading File
Reading File
Finding Sources
Searching the Web

DEWS II 2017 dry eye definition classification management stepwise guidelines

Searching PubMed

"dry eye" AND "DEWS" AND classification management

Reading File
Reading File
Searching PubMed

"dry eye disease" AND "TFOS DEWS"

Reading File
Reading File
Reading File
Reading File
Reading File
Reading File
Reading File
Reading File
Running Command

mkdir -p /tmp/workspace/dry-eye-pdf && ls /tmp/workspace/dry-eye-pdf/

Writing File

~/dry-eye-pdf/generate_dry_eye_pdf.py

from reportlab.lib.pagesizes import A4
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.units import cm, mm
from reportlab.lib.colors import HexColor, white, black
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 BaseDocTemplate, Frame, PageTemplate
from reportlab.lib import colors

# ─── COLOR PALETTE ───────────────────────────────────────────────────────────
DARK_BLUE   = HexColor("#0D3B66")
MID_BLUE    = HexColor("#1565C0")
LIGHT_BLUE  = HexColor("#E3F2FD")
TEAL        = HexColor("#00838F")
TEAL_LIGHT  = HexColor("#E0F7FA")
ORANGE      = HexColor("#E65100")
ORANGE_SOFT = HexColor("#FFF3E0")
GREY_DARK   = HexColor("#37474F")
GREY_MID    = HexColor("#78909C")
GREY_LIGHT  = HexColor("#ECEFF1")
GREEN       = HexColor("#2E7D32")
GREEN_LIGHT = HexColor("#E8F5E9")
RED_LIGHT   = HexColor("#FFEBEE")
RED         = HexColor("#C62828")

PAGE_W, PAGE_H = A4

# ─── STYLES ──────────────────────────────────────────────────────────────────
styles = getSampleStyleSheet()

def make_style(name, parent='Normal', **kwargs):
    s = ParagraphStyle(name, parent=styles[parent], **kwargs)
    return s

title_style = make_style('TitleMain',
    fontSize=26, leading=32, textColor=white,
    fontName='Helvetica-Bold', alignment=TA_CENTER, spaceAfter=4)

subtitle_style = make_style('TitleSub',
    fontSize=13, leading=18, textColor=HexColor("#BBDEFB"),
    fontName='Helvetica', alignment=TA_CENTER, spaceAfter=2)

date_style = make_style('DateStyle',
    fontSize=10, leading=14, textColor=HexColor("#90CAF9"),
    fontName='Helvetica-Oblique', alignment=TA_CENTER)

h1_style = make_style('H1',
    fontSize=16, leading=22, textColor=white,
    fontName='Helvetica-Bold', alignment=TA_LEFT,
    spaceBefore=4, spaceAfter=4)

h2_style = make_style('H2',
    fontSize=12, leading=17, textColor=DARK_BLUE,
    fontName='Helvetica-Bold', alignment=TA_LEFT,
    spaceBefore=8, spaceAfter=3)

h3_style = make_style('H3',
    fontSize=10.5, leading=15, textColor=TEAL,
    fontName='Helvetica-Bold', alignment=TA_LEFT,
    spaceBefore=6, spaceAfter=2)

body_style = make_style('Body',
    fontSize=9.5, leading=14, textColor=GREY_DARK,
    fontName='Helvetica', alignment=TA_JUSTIFY,
    spaceBefore=2, spaceAfter=2)

bullet_style = make_style('Bullet',
    fontSize=9.5, leading=14, textColor=GREY_DARK,
    fontName='Helvetica', alignment=TA_LEFT,
    leftIndent=14, firstLineIndent=-10,
    spaceBefore=1, spaceAfter=1)

sub_bullet_style = make_style('SubBullet',
    fontSize=9, leading=13, textColor=GREY_DARK,
    fontName='Helvetica', alignment=TA_LEFT,
    leftIndent=28, firstLineIndent=-10,
    spaceBefore=1, spaceAfter=1)

tip_style = make_style('Tip',
    fontSize=9.5, leading=14, textColor=DARK_BLUE,
    fontName='Helvetica-Oblique', alignment=TA_LEFT,
    leftIndent=8, rightIndent=8,
    spaceBefore=4, spaceAfter=4)

ref_style = make_style('Ref',
    fontSize=8, leading=11, textColor=GREY_MID,
    fontName='Helvetica-Oblique', alignment=TA_LEFT,
    spaceBefore=1, spaceAfter=1)

footer_style = make_style('Footer',
    fontSize=7.5, leading=10, textColor=GREY_MID,
    fontName='Helvetica', alignment=TA_CENTER)

caption_style = make_style('Caption',
    fontSize=8.5, leading=12, textColor=GREY_MID,
    fontName='Helvetica-Oblique', alignment=TA_CENTER,
    spaceBefore=2, spaceAfter=4)

# ─── HELPERS ─────────────────────────────────────────────────────────────────
def section_header(text, color=DARK_BLUE):
    """Blue banner header for major sections."""
    data = [[Paragraph(text, h1_style)]]
    t = Table(data, colWidths=[PAGE_W - 4*cm])
    t.setStyle(TableStyle([
        ('BACKGROUND', (0,0), (-1,-1), color),
        ('ROWPADDING', (0,0), (-1,-1), [8, 6, 8, 6]),
        ('BOX', (0,0), (-1,-1), 0, color),
    ]))
    return t

def info_box(title, items, bg=LIGHT_BLUE, title_color=MID_BLUE, bullet='•'):
    """Colored box with title and bullet list."""
    content = [Paragraph(f'<b>{title}</b>', make_style('BoxTitle',
        fontSize=10, leading=14, textColor=title_color,
        fontName='Helvetica-Bold', spaceBefore=0, spaceAfter=4))]
    for item in items:
        content.append(Paragraph(f'{bullet} {item}', make_style('BoxBullet',
            fontSize=9, leading=13, textColor=GREY_DARK,
            fontName='Helvetica', leftIndent=8, firstLineIndent=0,
            spaceBefore=1, spaceAfter=1)))
    data = [[content]]
    t = Table(data, colWidths=[PAGE_W - 4*cm])
    t.setStyle(TableStyle([
        ('BACKGROUND', (0,0), (-1,-1), bg),
        ('ROWPADDING', (0,0), (-1,-1), [10, 8, 10, 8]),
        ('BOX', (0,0), (-1,-1), 1.5, title_color),
        ('ROUNDEDCORNERS', [4]),
    ]))
    return t

def two_col_table(left_items, right_items, left_title, right_title,
                  left_bg=LIGHT_BLUE, right_bg=TEAL_LIGHT,
                  left_tc=MID_BLUE, right_tc=TEAL):
    def make_cell(title, items, bg, tc):
        cell = [Paragraph(f'<b>{title}</b>', make_style('CT',
            fontSize=10, leading=14, textColor=tc,
            fontName='Helvetica-Bold', spaceBefore=0, spaceAfter=4))]
        for i in items:
            cell.append(Paragraph(f'• {i}', make_style('CB',
                fontSize=9, leading=13, textColor=GREY_DARK,
                fontName='Helvetica', leftIndent=8, spaceBefore=1, spaceAfter=1)))
        return cell
    data = [[make_cell(left_title, left_items, left_bg, left_tc),
             make_cell(right_title, right_items, right_bg, right_tc)]]
    col_w = (PAGE_W - 4*cm - 0.4*cm) / 2
    t = Table(data, colWidths=[col_w, col_w], hAlign='LEFT')
    t.setStyle(TableStyle([
        ('BACKGROUND', (0,0), (0,-1), left_bg),
        ('BACKGROUND', (1,0), (1,-1), right_bg),
        ('VALIGN', (0,0), (-1,-1), 'TOP'),
        ('ROWPADDING', (0,0), (-1,-1), [8, 6, 8, 6]),
        ('BOX', (0,0), (0,-1), 1.5, left_tc),
        ('BOX', (1,0), (1,-1), 1.5, right_tc),
        ('LEFTPADDING', (0,0), (-1,-1), 8),
        ('RIGHTPADDING', (0,0), (-1,-1), 8),
    ]))
    return t

def level_box(level_num, title, items, colors_map):
    bg, tc, border = colors_map.get(level_num, (GREY_LIGHT, GREY_DARK, GREY_MID))
    label = Paragraph(f'<b>LEVEL {level_num}</b>', make_style(f'L{level_num}Label',
        fontSize=22, leading=26, textColor=tc,
        fontName='Helvetica-Bold', alignment=TA_CENTER))
    sub = Paragraph(title, make_style(f'L{level_num}Sub',
        fontSize=10, leading=13, textColor=tc,
        fontName='Helvetica-Bold', alignment=TA_CENTER))
    bullets = []
    for i in items:
        bullets.append(Paragraph(f'• {i}', make_style(f'L{level_num}B',
            fontSize=9, leading=13, textColor=GREY_DARK,
            fontName='Helvetica', leftIndent=8, spaceBefore=1, spaceAfter=1)))
    left_cell = [label, Spacer(1, 4), sub]
    right_cell = bullets
    col_w_l = 3.5*cm
    col_w_r = (PAGE_W - 4*cm) - col_w_l - 0.2*cm
    data = [[left_cell, right_cell]]
    t = Table(data, colWidths=[col_w_l, col_w_r])
    t.setStyle(TableStyle([
        ('BACKGROUND', (0,0), (0,-1), bg),
        ('BACKGROUND', (1,0), (1,-1), white),
        ('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
        ('VALIGN', (1,0), (1,-1), 'TOP'),
        ('ROWPADDING', (0,0), (-1,-1), [8, 8, 8, 8]),
        ('BOX', (0,0), (-1,-1), 2, border),
        ('LINEAFTER', (0,0), (0,-1), 1, border),
        ('LEFTPADDING', (0,0), (0,-1), 6),
        ('LEFTPADDING', (1,0), (1,-1), 10),
    ]))
    return t

# ─── PAGE TEMPLATE ───────────────────────────────────────────────────────────
def add_page_decorations(canvas, doc):
    canvas.saveState()
    # Top thin stripe
    canvas.setFillColor(DARK_BLUE)
    canvas.rect(0, PAGE_H - 6*mm, PAGE_W, 6*mm, fill=1, stroke=0)
    # Bottom footer bar
    canvas.setFillColor(GREY_LIGHT)
    canvas.rect(0, 0, PAGE_W, 1.2*cm, fill=1, stroke=0)
    canvas.setFillColor(GREY_MID)
    canvas.setFont('Helvetica', 7.5)
    canvas.drawCentredString(PAGE_W/2, 0.45*cm,
        f'Dry Eye Disease - A Clinical Reference | TFOS DEWS II Framework | Page {doc.page}')
    canvas.restoreState()

def build_cover(canvas, doc):
    canvas.saveState()
    # Full-page gradient-like background using two rects
    canvas.setFillColor(DARK_BLUE)
    canvas.rect(0, 0, PAGE_W, PAGE_H, fill=1, stroke=0)
    canvas.setFillColor(MID_BLUE)
    canvas.rect(0, 0, PAGE_W, PAGE_H * 0.45, fill=1, stroke=0)
    # Decorative circles
    canvas.setFillColor(HexColor("#1976D2"))
    canvas.circle(PAGE_W - 3*cm, PAGE_H - 3*cm, 5*cm, fill=1, stroke=0)
    canvas.setFillColor(HexColor("#1565C0"))
    canvas.circle(3*cm, 4*cm, 3.5*cm, fill=1, stroke=0)
    canvas.restoreState()

# ─── BUILD THE PDF ────────────────────────────────────────────────────────────
OUTPUT = '/tmp/workspace/dry-eye-pdf/Dry_Eye_Disease_Clinical_Reference.pdf'

doc = BaseDocTemplate(
    OUTPUT,
    pagesize=A4,
    rightMargin=2*cm, leftMargin=2*cm,
    topMargin=1.5*cm, bottomMargin=1.8*cm,
)

cover_frame = Frame(0, 0, PAGE_W, PAGE_H, id='cover')
content_frame = Frame(2*cm, 1.8*cm, PAGE_W - 4*cm, PAGE_H - 3.3*cm, id='content')

cover_template = PageTemplate(id='Cover', frames=[cover_frame], onPage=build_cover)
main_template  = PageTemplate(id='Main',  frames=[content_frame], onPage=add_page_decorations)
doc.addPageTemplates([cover_template, main_template])

story = []

# ══════════════════════════════════════════════════════════════════════════════
# COVER PAGE
# ══════════════════════════════════════════════════════════════════════════════
story.append(Spacer(1, 5*cm))
story.append(Paragraph("DRY EYE DISEASE", title_style))
story.append(Spacer(1, 0.5*cm))
story.append(Paragraph("A Comprehensive Clinical Reference", subtitle_style))
story.append(Spacer(1, 0.3*cm))
story.append(Paragraph(
    "Definition · Classification · Evaluation · Stepwise Management",
    subtitle_style))
story.append(Spacer(1, 0.6*cm))
story.append(HRFlowable(width="70%", thickness=1.5, color=HexColor("#64B5F6"),
                         hAlign='CENTER'))
story.append(Spacer(1, 0.6*cm))
story.append(Paragraph(
    "Based on the TFOS DEWS II (Tear Film & Ocular Surface Society<br/>"
    "Dry Eye Workshop II, 2017) International Guidelines",
    subtitle_style))
story.append(Spacer(1, 1.2*cm))
story.append(Paragraph(
    "References: Kanski's Clinical Ophthalmology (10th Ed.) · "
    "Wills Eye Manual (8th Ed.) · TFOS DEWS II Report",
    date_style))
story.append(Spacer(1, 0.4*cm))
story.append(Paragraph("July 2026", date_style))

story.append(PageBreak())

# ══════════════════════════════════════════════════════════════════════════════
# SECTION 1: DEFINITION & OVERVIEW
# ══════════════════════════════════════════════════════════════════════════════
story.append(section_header("1. DEFINITION AND OVERVIEW", DARK_BLUE))
story.append(Spacer(1, 0.35*cm))

story.append(Paragraph("TFOS DEWS II Definition (2017)", h2_style))
story.append(Paragraph(
    "Dry eye is a <b>multifactorial disease of the ocular surface</b> "
    "characterised by a loss of homeostasis of the tear film, and accompanied by "
    "ocular symptoms, in which <b>tear film instability and hyperosmolarity, "
    "ocular surface inflammation and damage, and neurosensory abnormalities</b> "
    "play etiological roles.",
    body_style))
story.append(Spacer(1, 0.2*cm))

# Definition box
story.append(info_box(
    "Key Conceptual Pillars of DED (DEWS II)",
    [
        "Loss of tear film HOMEOSTASIS is the central concept",
        "Ocular symptoms must be present (distinguishes from asymptomatic tear dysfunction)",
        "Tear film instability → hyperosmolarity → inflammation → ocular surface damage",
        "Neurosensory abnormalities recognized as etiological contributors",
        "Positive feedback (vicious cycle) perpetuates and amplifies the disease",
    ],
    bg=LIGHT_BLUE, title_color=DARK_BLUE))
story.append(Spacer(1, 0.3*cm))

story.append(Paragraph("Tear Film Structure and Function", h2_style))
story.append(Paragraph(
    "The tear film is a complex, trilaminar fluid layer covering the ocular surface, "
    "composed of three interacting layers:",
    body_style))
story.append(Spacer(1, 0.15*cm))

tear_data = [
    ["Layer", "Source", "Composition", "Function"],
    ["Lipid (Outer)", "Meibomian glands\n(tarsal glands)", "Phospholipids, triglycerides,\ncholesterol esters, wax esters",
     "Retards evaporation;\nstabilizes tear film; smooths optical surface"],
    ["Aqueous (Middle)", "Main & accessory\nlacrimal glands", "Water, electrolytes, proteins\n(lactoferrin, lysozyme, IgA)",
     "Nourishes cornea; dilutes irritants;\nantimicrobial; oxygen supply"],
    ["Mucin (Inner)", "Goblet cells;\ncorneal epithelium",
     "Transmembrane mucins (MUC1,4,16);\nsecreted mucins (MUC5AC)",
     "Converts hydrophobic surface to hydrophilic;\nfacilitates wetting and lubrication"],
]

tear_table = Table(tear_data, colWidths=[2.8*cm, 3.2*cm, 4.2*cm, 4.8*cm])
tear_table.setStyle(TableStyle([
    ('BACKGROUND', (0,0), (-1,0), DARK_BLUE),
    ('TEXTCOLOR',  (0,0), (-1,0), white),
    ('FONTNAME',   (0,0), (-1,0), 'Helvetica-Bold'),
    ('FONTSIZE',   (0,0), (-1,0), 9),
    ('ALIGN',      (0,0), (-1,-1), 'LEFT'),
    ('FONTNAME',   (0,1), (-1,-1), 'Helvetica'),
    ('FONTSIZE',   (0,1), (-1,-1), 8.5),
    ('ROWBACKGROUNDS', (0,1), (-1,-1), [white, GREY_LIGHT]),
    ('GRID',       (0,0), (-1,-1), 0.5, GREY_MID),
    ('ROWPADDING', (0,0), (-1,-1), [5, 4, 5, 4]),
    ('VALIGN',     (0,0), (-1,-1), 'TOP'),
]))
story.append(tear_table)
story.append(Spacer(1, 0.25*cm))

story.append(Paragraph("Pathophysiology - The Vicious Cycle", h2_style))
story.append(Paragraph(
    "The four core, inter-related mechanisms responsible for dry eye are:",
    body_style))
story.append(Spacer(1, 0.1*cm))

path_items = [
    ("Tear Instability", "Reduced tear film break-up time; loss of surface mucins; meibomian gland dysfunction"),
    ("Tear Hyperosmolarity", "Core mechanism; damages epithelial cells directly and triggers inflammatory cascades; "
     "threshold ~308 mOsm/L (normal); >316 mOsm/L = moderate-severe"),
    ("Ocular Surface Inflammation", "Present in 80% of KCS patients; both cause and consequence of dry eye; "
     "involves T-cell activation, pro-inflammatory cytokines (IL-1, IL-6, TNF-α), MMP-9 upregulation"),
    ("Ocular Surface Damage", "Punctate epithelial erosions, mucous plaques, corneal filaments, goblet cell loss; "
     "perpetuates the vicious cycle"),
]
for title, detail in path_items:
    story.append(Paragraph(f'<b>• {title}:</b> {detail}', bullet_style))

story.append(Spacer(1, 0.2*cm))
story.append(info_box(
    "Clinical Tip (Kanski's)",
    ["Whether a patient has primary aqueous deficiency or evaporative dry eye, "
     "the end result is ocular surface inflammation and discomfort.",
     "There is a poor correlation between symptoms and clinical tests; "
     "reliability improves as severity increases."],
    bg=ORANGE_SOFT, title_color=ORANGE))

story.append(PageBreak())

# ══════════════════════════════════════════════════════════════════════════════
# SECTION 2: CLASSIFICATION
# ══════════════════════════════════════════════════════════════════════════════
story.append(section_header("2. CLASSIFICATION (DEWS / DEWS II)", MID_BLUE))
story.append(Spacer(1, 0.35*cm))

story.append(Paragraph(
    "The DEWS (2007) and updated DEWS II (2017) classification divides dry eye into two "
    "primary etiological subtypes, recognizing that most patients present with "
    "significant overlap between mechanisms.",
    body_style))
story.append(Spacer(1, 0.25*cm))

story.append(two_col_table(
    left_title="I. AQUEOUS-DEFICIENT DRY EYE (ADDE)",
    left_items=[
        "Sjögren Syndrome (SS) DED",
        "   Primary SS (isolated)",
        "   Secondary SS (RA, SLE, PSC)",
        "",
        "Non-Sjögren Syndrome DED:",
        "Lacrimal deficiency - Primary:",
        "   Age-related dry eye",
        "   Congenital alacrima",
        "   Familial dysautonomia (Riley-Day)",
        "Lacrimal deficiency - Secondary:",
        "   Inflammatory/neoplastic infiltration",
        "   AIDS, GVHD",
        "   Lacrimal gland/nerve ablation",
        "Lacrimal duct obstruction:",
        "   Trachoma, cicatricial pemphigoid",
        "   Chemical burns, Stevens-Johnson",
        "Reflex hyposecretion - Sensory:",
        "   Contact lens wear, diabetes",
        "   Refractive surgery, neurotrophic keratitis",
        "Reflex hyposecretion - Motor:",
        "   CN VII palsy, systemic drugs",
    ],
    right_title="II. EVAPORATIVE DRY EYE (EDE)",
    right_items=[
        "A. INTRINSIC CAUSES:",
        "Meibomian Gland Dysfunction (MGD):",
        "   Posterior blepharitis, rosacea",
        "Lid aperture disorders:",
        "   Scleral show, lid retraction",
        "   Proptosis, facial nerve palsy",
        "Low blink rate:",
        "   Parkinson disease",
        "   VDU/computer/reading use",
        "Drug-induced evaporation:",
        "   Antihistamines, beta-blockers",
        "   Antispasmodics, diuretics",
        "",
        "B. EXTRINSIC CAUSES:",
        "Vitamin A deficiency",
        "Topical drug toxicity / preservatives",
        "Contact lens wear",
        "Ocular surface disease (allergic conjunctivitis)",
    ],
    left_bg=LIGHT_BLUE, right_bg=TEAL_LIGHT,
    left_tc=MID_BLUE, right_tc=TEAL))

story.append(Spacer(1, 0.3*cm))

story.append(Paragraph("DEWS II Severity Grading", h2_style))
story.append(Paragraph(
    "DEWS/DEWS II grade severity from 1 to 4. The Ocular Surface Disease Index (OSDI) "
    "questionnaire and clinical findings are used together for grading. "
    "Treatment advances stepwise through these levels.",
    body_style))
story.append(Spacer(1, 0.15*cm))

sev_data = [
    ["Grade", "Symptoms", "Conjunctival Staining", "Corneal Staining", "TBUT\n(sec)", "Schirmer\n(mm/5min)"],
    ["1\nMild", "Mild / episodic\nenvironmental triggers", "None–mild", "None", ">10", ">10"],
    ["2\nModerate", "Moderate / episodic\nor chronic", "None–mild", "None–mild\n(peripheral)", "<10", ">10 or\nborderline"],
    ["3\nSevere", "Frequent/chronic;\nactivity limitation", "Moderate–severe", "Moderate\n(central)", "<5", "<5"],
    ["4\nVery Severe", "Constant; disabling;\nwithout symptom relief", "Severe with\ncorneal features", "Severe\n(filaments)", "Immediate", "<2"],
]

sev_table = Table(sev_data, colWidths=[1.8*cm, 3.5*cm, 3.5*cm, 3*cm, 1.7*cm, 1.5*cm])
sev_table.setStyle(TableStyle([
    ('BACKGROUND', (0,0), (-1,0), DARK_BLUE),
    ('TEXTCOLOR',  (0,0), (-1,0), white),
    ('FONTNAME',   (0,0), (-1,0), 'Helvetica-Bold'),
    ('FONTSIZE',   (0,0), (-1,0), 8.5),
    ('ALIGN',      (0,0), (-1,-1), 'CENTER'),
    ('VALIGN',     (0,0), (-1,-1), 'MIDDLE'),
    ('FONTNAME',   (0,1), (-1,-1), 'Helvetica'),
    ('FONTSIZE',   (0,1), (-1,-1), 8),
    ('BACKGROUND', (0,1), (-1,1), HexColor("#E8F5E9")),
    ('BACKGROUND', (0,2), (-1,2), HexColor("#FFF9C4")),
    ('BACKGROUND', (0,3), (-1,3), HexColor("#FFE0B2")),
    ('BACKGROUND', (0,4), (-1,4), HexColor("#FFCDD2")),
    ('GRID',       (0,0), (-1,-1), 0.5, GREY_MID),
    ('ROWPADDING', (0,0), (-1,-1), [5, 4, 5, 4]),
]))
story.append(sev_table)

story.append(PageBreak())

# ══════════════════════════════════════════════════════════════════════════════
# SECTION 3: EVALUATION / WORKUP
# ══════════════════════════════════════════════════════════════════════════════
story.append(section_header("3. EVALUATION AND DIAGNOSIS", TEAL))
story.append(Spacer(1, 0.35*cm))

story.append(Paragraph(
    "A systematic approach to evaluation integrates patient symptoms, "
    "clinical signs, and objective tests. DEWS II recommends a "
    "triaging approach: screening questionnaires → diagnostic tests "
    "performed in a sequence that minimises confounding.",
    body_style))
story.append(Spacer(1, 0.25*cm))

story.append(Paragraph("3.1  Symptoms and History", h2_style))
story.append(Paragraph(
    "Characteristic symptoms include burning, dryness, grittiness, foreign body sensation, "
    "mildly decreased vision, and paradoxical excess tearing (reflex). Symptoms "
    "worsen with environmental triggers (wind, air-conditioning, central heating, smoke, "
    "low humidity) and prolonged visual tasks that reduce blink rate "
    "(reading, computer use, watching TV).",
    body_style))
story.append(Spacer(1, 0.1*cm))

story.append(info_box(
    "Validated Questionnaires",
    [
        "OSDI (Ocular Surface Disease Index) - 12 items; score 0-100; "
        ">12 = mild DED, >23 = moderate, >32 = severe",
        "SPEED (Standard Patient Evaluation of Eye Dryness)",
        "DEQ-5 (Dry Eye Questionnaire 5)",
        "Symptom Assessment in Dry Eye (SANDE)",
    ],
    bg=LIGHT_BLUE, title_color=MID_BLUE))
story.append(Spacer(1, 0.25*cm))

story.append(Paragraph("3.2  Clinical Examination - Recommended Test Sequence", h2_style))
story.append(Paragraph(
    "Tests should be performed in the following order to minimise confounding "
    "(Schirmer strips can damage the ocular surface and affect staining results):",
    body_style))
story.append(Spacer(1, 0.15*cm))

# Workup sequence table
workup_data = [
    ["Step", "Test", "Method / Interpretation", "Normal / Cutoff"],
    ["1", "Tear Meniscus Height\n(TMH)", "Slit lamp: height at inferior lid margin;\nassess before any drops",
     "≥ 0.2 mm (DEWS II)\n≥ 0.5 mm (Wills Eye)"],
    ["2", "Tear Film Break-up\nTime (TBUT / NIKBUT)",
     "Fluorescein 2% / impregnated strip + non-preserved saline;\nbroad cobalt blue beam; time from last blink to first dry spot\n"
     "(Non-invasive TBUT preferred in DEWS II)",
     "< 10 sec = suspicious\n< 5 sec = severe\n(NIKBUT < 10 sec = abnormal)"],
    ["3", "Ocular Surface\nStaining",
     "Fluorescein: corneal/conjunctival epithelial damage\n"
     "Lissamine green: devitalized cells (preferred; less irritation)\n"
     "Rose Bengal: dead/devitalized cells (intense staining; stings)",
     "Oxford / van Bijsterveld grading;\n"
     "Interpalpebral pattern = ADDE;\nInferior = blepharitis/exposure"],
    ["4", "Tear Osmolarity",
     "TearLab™ or similar; collected from tear meniscus;\ninter-eye difference > 8 mOsm/L also significant",
     "Normal ≤ 308 mOsm/L\n308-316 = mild-moderate\n> 316 = moderate-severe"],
    ["5", "MMP-9\n(InflammaDry)",
     "Point-of-care test; elevated MMP-9 indicates\nocular surface inflammation",
     "≥ 40 ng/mL = positive;\nsensitivity 85%, specificity 94%"],
    ["6", "Schirmer Test",
     "No. 41 Whatman paper (5 mm × 35 mm); lower lid junction of\nmiddle & lateral 1/3; 5 min; eyes open with blinking\n"
     "Schirmer I (without anaesthetic): basal + reflex\n"
     "Schirmer II (with proparacaine): basal secretion only",
     "Schirmer I: ≥ 15 mm/5 min\nSchirmer II: ≥ 6 mm/5 min\n(< 5 mm = severely abnormal)"],
    ["7", "Meibomian Gland\nEvaluation",
     "Meibography (IR transillumination): gland dropout grading\nMeibum expression: quality (clear → turbid → toothpaste)\nLid margin: vascular engorgement, notching, plugging",
     "Meiboscore 0-3 per lid\n(3 = >67% dropout)"],
    ["8", "Lid Wiper\nEpitheliopathy",
     "Lissamine green staining of upper tarsal conjunctival\nwiper zone; friction marker",
     "Any staining ≥ 2 mm wide\nor ≥ 25% circumference = positive"],
]

workup_table = Table(workup_data, colWidths=[0.8*cm, 3.0*cm, 7.0*cm, 4.2*cm])
workup_table.setStyle(TableStyle([
    ('BACKGROUND', (0,0), (-1,0), TEAL),
    ('TEXTCOLOR',  (0,0), (-1,0), white),
    ('FONTNAME',   (0,0), (-1,0), 'Helvetica-Bold'),
    ('FONTSIZE',   (0,0), (-1,0), 9),
    ('ALIGN',      (0,0), (0,-1), 'CENTER'),
    ('ALIGN',      (1,0), (-1,0), 'CENTER'),
    ('ALIGN',      (1,1), (-1,-1), 'LEFT'),
    ('VALIGN',     (0,0), (-1,-1), 'TOP'),
    ('FONTNAME',   (0,1), (-1,-1), 'Helvetica'),
    ('FONTSIZE',   (0,1), (-1,-1), 8.5),
    ('ROWBACKGROUNDS', (0,1), (-1,-1), [white, TEAL_LIGHT]),
    ('GRID',       (0,0), (-1,-1), 0.5, GREY_MID),
    ('ROWPADDING', (0,0), (-1,-1), [5, 4, 5, 4]),
    ('FONTNAME',   (0,1), (0,-1), 'Helvetica-Bold'),
    ('TEXTCOLOR',  (0,1), (0,-1), TEAL),
]))
story.append(workup_table)
story.append(Spacer(1, 0.25*cm))

story.append(Paragraph("3.3  Additional / Ancillary Investigations", h2_style))
story.append(Paragraph(
    "The following are used in selected cases or research settings:",
    body_style))
additional_tests = [
    "Phenol red thread test: pH-sensitive thread; 15 sec; < 6 mm = abnormal (faster than Schirmer)",
    "Tear lactoferrin: Low levels suggest ADDE (ELISA or Lactoferrin InflammaDry)",
    "Fluorescein clearance test / Tear function index: Delayed clearance in all dry eye states",
    "Impression cytology: Goblet cell density; squamous metaplasia grading",
    "In-vivo confocal microscopy (IVCM): Corneal nerve density; sub-basal nerve plexus; Langerhans cells",
    "Anterior segment OCT: Quantitative tear meniscus height/volume",
    "Systemic workup for Sjögren syndrome: Anti-SSA/SSB antibodies, ANA, RF, minor salivary gland biopsy",
]
for t in additional_tests:
    story.append(Paragraph(f"• {t}", bullet_style))

story.append(PageBreak())

# ══════════════════════════════════════════════════════════════════════════════
# SECTION 4: STEPWISE MANAGEMENT
# ══════════════════════════════════════════════════════════════════════════════
story.append(section_header("4. STEPWISE MANAGEMENT (DEWS II FRAMEWORK)", GREEN))
story.append(Spacer(1, 0.35*cm))

story.append(Paragraph(
    "The DEWS and DEWS II guidelines provide a severity-based, stepwise treatment algorithm. "
    "Management proceeds to the next level if preceding measures are inadequate. "
    "The underlying processes are generally not reversible; management aims to "
    "control symptoms and prevent surface damage.",
    body_style))
story.append(Spacer(1, 0.15*cm))

level_colors = {
    1: (GREEN_LIGHT, GREEN, GREEN),
    2: (LIGHT_BLUE, MID_BLUE, MID_BLUE),
    3: (ORANGE_SOFT, ORANGE, ORANGE),
    4: (RED_LIGHT, RED, RED),
}

# Level 1
story.append(level_box(1, "EDUCATION, ENVIRONMENT & FIRST-LINE THERAPIES\n(All Patients - Grade 1)", [
    "Patient education: realistic expectations, importance of compliance",
    "Blink exercises: conscious blinking during reading, VDT use, TV viewing",
    "Screen positioning: below eye level to reduce palpebral aperture size",
    "Lifestyle modification: humidifiers, avoid fans/AC drafts, protect from wind",
    "Systemic medication review: identify and eliminate contributory drugs",
    "Discontinue toxic/preserved topical medications where possible",
    "Artificial tear substitutes (preserved drops, gels, ointments at HS)",
    "Basic eyelid therapy: warm compresses + lid hygiene for blepharitis/MGD",
    "Reparative lid surgery if needed: entropion, ectropion, lagophthalmos",
    "Caution: advise against laser refractive surgery in DED patients",
    "Contact lens: replace or modify wearing schedule; switch to daily disposables",
    "Dietary: Omega-3 supplementation (fish oil 1-3g/day)",
], level_colors))
story.append(Spacer(1, 0.25*cm))

# Level 2
story.append(level_box(2, "ANTI-INFLAMMATORY AND SUPPLEMENTAL THERAPIES\n(Grades 2-3)", [
    "Non-preserved artificial tears (up to q1-2h PRN)",
    "Lubricating gel or ointment q.h.s.",
    "CYCLOSPORINE A 0.05% or 0.09% (Restasis / Cequa) b.i.d.:",
    "   Indicated for chronic DED with decreased tears secondary to ocular inflammation",
    "   Burns on instillation for first weeks; onset 1-3 months",
    "   Consider concurrent mild topical steroid (loteprednol 0.5% or FML 0.1%) for 1 month",
    "LIFITEGRAST 5% (Xiidra) b.i.d.: LFA-1 integrin antagonist; anti-inflammatory",
    "   Symptomatic improvement within 2 weeks; full effect up to 3 months",
    "   May cause instillation burn, blurred vision, metallic taste",
    "Topical steroids (short-course): FML 0.1%, loteprednol 0.5%, prednisolone 1%",
    "   For acute exacerbations only; monitor IOP; avoid prolonged use",
    "Tetracyclines (doxycycline 50-100 mg/day): for MGD, rosacea-associated DED",
    "   Anti-MMP and anti-inflammatory properties; minimum 6-8 weeks",
    "Secretagogues: pilocarpine 5 mg QID, cevimeline 30 mg TID (for Sjögren)",
    "Rebamipide 2% QID: mucin secretagogue; improves goblet cell density",
    "Punctal plugs (collagen temporary or silicone/acrylic reversible):",
    "   Occludes lower punctum first; reduces tear drainage",
    "   Treat active inflammation/blepharitis BEFORE plugging",
    "Moisture chamber spectacles / side shields",
    "Oral omega-3 fatty acids (if not already started at Level 1)",
], level_colors))
story.append(Spacer(1, 0.25*cm))

# Level 3
story.append(level_box(3, "ADVANCED THERAPIES FOR REFRACTORY DISEASE\n(Grade 3 - Severe)", [
    "AUTOLOGOUS SERUM EYE DROPS (20-100% concentration):",
    "   Rich in growth factors (EGF, TGF-β), vitamins, fibronectin",
    "   Promotes epithelial healing; use q.i.d. to q2h",
    "   Requires refrigeration; short shelf life (3-6 months frozen)",
    "UMBILICAL CORD SERUM: similar to autologous; higher growth factor content",
    "SCLERAL CONTACT LENSES: fluid reservoir over cornea; excellent for severe/irregular surface",
    "Bandage soft contact lens: for erosions, filamentary keratitis",
    "Permanent punctal occlusion:",
    "   Thermal/argon laser cautery if plugs repeatedly fall out",
    "   Occlude both upper and lower puncta if needed",
    "MANAGEMENT OF COMPLICATIONS:",
    "Filamentary keratitis: remove filaments with forceps + acetylcysteine 10% q.i.d. (mucolytic)",
    "Corneal erosions: bandage lens, preservative-free lubricants, vitamin A drops",
    "Topical vitamin A (retinol palmitate): promotes epithelial and goblet cell recovery",
], level_colors))
story.append(Spacer(1, 0.25*cm))

# Level 4
story.append(level_box(4, "SYSTEMIC AND SURGICAL INTERVENTIONS\n(Grade 4 - Very Severe / Refractory)", [
    "Systemic anti-inflammatory agents (for Sjögren, cicatricial diseases):",
    "   Hydroxychloroquine, azathioprine, mycophenolate mofetil, rituximab",
    "TARSORRHAPHY:",
    "   Temporary adhesive tape tarsorrhaphy (lateral 1/3 of lids, pending surgery)",
    "   Permanent lateral tarsorrhaphy for refractory exposure and evaporative DED",
    "SALIVARY GLAND TRANSPLANTATION: labial salivary gland grafts to conjunctiva",
    "AMNIOTIC MEMBRANE TRANSPLANTATION: for persistent epithelial defects",
    "CONJUNCTIVAL/LIMBAL TRANSPLANTATION: for severe cicatricial disease",
    "MUCOUS MEMBRANE GRAFTING: in severe cicatricial pemphigoid, Stevens-Johnson",
    "Botulinum toxin injection for punctal closure (experimental)",
    "Meibomian gland probing/intraductal probing for refractory MGD",
    "Intense pulsed light (IPL): for posterior blepharitis/MGD-associated EDE",
    "LipiFlow thermal pulsation: meibomian gland expression/therapy",
], level_colors))

story.append(Spacer(1, 0.3*cm))

story.append(Paragraph("Summary: Wills Eye Manual Severity-Based Quick Guide", h2_style))
quick_data = [
    ["Severity", "Key Treatments"],
    ["MILD\n(Grade 1)", "Artificial tears q.i.d. (preserved OK)\nEnvironmental/lifestyle modification\nLid hygiene if blepharitis present"],
    ["MODERATE\n(Grade 2)", "Preservative-free artificial tears up to q1-2h\nLubricating gel/ointment q.h.s.\nCyclosporine 0.05% or 0.09% b.i.d.\nLifitegrast 5% b.i.d.\nLifestyle modification (humidifiers, smoking cessation)\nPunctal occlusion if above measures inadequate"],
    ["SEVERE\n(Grade 3-4)", "Cyclosporine or lifitegrast (continued)\nPunctal occlusion (lower + upper puncta; thermal cautery if plugs fail)\nLubricating gel b.i.d.-q.i.d. p.r.n.\nMoisture chamber goggles\nRemove filaments; acetylcysteine 10% q.i.d. if filamentary keratitis\nOral omega-3, autologous serum, topical vitamin A\nBandage soft lens or scleral lens\nLateral tarsorrhaphy (temporary or permanent) if all else fails"],
]
quick_table = Table(quick_data, colWidths=[3.2*cm, 12.8*cm])
quick_table.setStyle(TableStyle([
    ('BACKGROUND', (0,0), (-1,0), GREEN),
    ('TEXTCOLOR',  (0,0), (-1,0), white),
    ('FONTNAME',   (0,0), (-1,0), 'Helvetica-Bold'),
    ('FONTSIZE',   (0,0), (-1,0), 9.5),
    ('ALIGN',      (0,0), (0,-1), 'CENTER'),
    ('ALIGN',      (1,1), (1,-1), 'LEFT'),
    ('VALIGN',     (0,0), (-1,-1), 'MIDDLE'),
    ('FONTNAME',   (0,1), (-1,-1), 'Helvetica'),
    ('FONTSIZE',   (0,1), (-1,-1), 8.5),
    ('BACKGROUND', (0,1), (-1,1), GREEN_LIGHT),
    ('BACKGROUND', (0,2), (-1,2), HexColor("#FFF9C4")),
    ('BACKGROUND', (0,3), (-1,3), HexColor("#FFCDD2")),
    ('GRID',       (0,0), (-1,-1), 0.5, GREY_MID),
    ('ROWPADDING', (0,0), (-1,-1), [6, 5, 6, 5]),
    ('FONTNAME',   (0,1), (0,-1), 'Helvetica-Bold'),
]))
story.append(quick_table)

story.append(PageBreak())

# ══════════════════════════════════════════════════════════════════════════════
# SECTION 5: SPECIAL SITUATIONS
# ══════════════════════════════════════════════════════════════════════════════
story.append(section_header("5. SPECIAL SITUATIONS AND MANAGEMENT PEARLS", HexColor("#6A1B9A")))
story.append(Spacer(1, 0.35*cm))

story.append(Paragraph("5.1  Sjögren Syndrome DED", h2_style))
story.append(Paragraph(
    "SS DED is typically severe ADDE. ACR criteria require: "
    "positive anti-SSA/SSB (or RF + ANA), ocular surface staining above threshold grade, "
    "and focal lymphocytic sialadenitis on minor salivary gland biopsy.",
    body_style))
ss_mgmt = [
    "All above topical therapies at appropriate severity level",
    "Cyclosporine 0.05% preferred given strong inflammatory component",
    "Punctal occlusion often required early",
    "Secretagogues: pilocarpine 5 mg QID or cevimeline 30 mg TID for systemic dryness",
    "Hydroxychloroquine for systemic manifestations",
    "Rheumatology referral essential; treat extraglandular manifestations",
    "Regular monitoring for lymphoma (increased risk in primary SS)",
]
for i in ss_mgmt:
    story.append(Paragraph(f"• {i}", bullet_style))
story.append(Spacer(1, 0.2*cm))

story.append(Paragraph("5.2  MGD-Predominant / Evaporative DED", h2_style))
mgd_mgmt = [
    "Warm compresses twice daily (45°C for 5-10 min) to liquefy meibum",
    "Lid massage and hygiene (lid scrubs, diluted baby shampoo, or commercial wipes)",
    "Omega-3 fatty acids (fish oil 1-3 g/day or flaxseed oil)",
    "Oral doxycycline 50-100 mg/day (anti-MMP properties; minimum 6-8 weeks)",
    "Azithromycin topical 1% (alternative to systemic tetracyclines)",
    "Intense Pulsed Light (IPL) therapy for rosacea-associated MGD",
    "LipiFlow vectored thermal pulsation device for meibomian gland obstruction",
    "Meibomian gland intraductal probing for scarring/obstruction",
]
for i in mgd_mgmt:
    story.append(Paragraph(f"• {i}", bullet_style))
story.append(Spacer(1, 0.2*cm))

story.append(Paragraph("5.3  Post-Refractive Surgery / Post-LASIK DED", h2_style))
story.append(Paragraph(
    "Caused by severing of corneal nerves during flap creation, reducing afferent reflex "
    "arc. SMILE has lower incidence than LASIK. Usually resolves within 6-12 months "
    "but may persist.",
    body_style))
post_surg = [
    "Frequent preservative-free artificial tears from day of surgery",
    "Cyclosporine 0.05% b.i.d. starting 1 month pre-operatively in at-risk patients",
    "Avoid LASIK in patients with pre-existing dry eye (relative contraindication)",
    "Punctal occlusion if symptoms persist beyond 3-6 months",
    "Autologous serum for recalcitrant post-LASIK neurotrophic epitheliopathy",
]
for i in post_surg:
    story.append(Paragraph(f"• {i}", bullet_style))
story.append(Spacer(1, 0.2*cm))

story.append(Paragraph("5.4  Filamentary Keratitis", h2_style))
story.append(Paragraph(
    "Mucous strands anchored to corneal epithelium; common in severe aqueous deficiency.",
    body_style))
fk = [
    "Remove filaments with jeweller's forceps at slit lamp",
    "Acetylcysteine 10% (mucolytic) q.i.d. eye drops",
    "Bandage soft contact lens",
    "Aggressive lubrication with preservative-free drops and ointment",
    "Address underlying cause (DED, prolonged eye closure, post-surgical)",
]
for i in fk:
    story.append(Paragraph(f"• {i}", bullet_style))
story.append(Spacer(1, 0.3*cm))

story.append(info_box(
    "Drug Classes That Can Cause or Worsen Dry Eye",
    [
        "Anticholinergics, antihistamines (H1 and H2 blockers)",
        "Beta-blockers (systemic and topical)",
        "Diuretics (reduces aqueous secretion)",
        "Antidepressants: TCAs, SSRIs",
        "Antipsychotics, antispasmodics",
        "Oral contraceptives, retinoids",
        "Isotretinoin (Accutane): reduces meibomian gland size and function",
        "Chemotherapy agents",
        "Topical preserved eye drops (BAC/BAK preservative)",
    ],
    bg=RED_LIGHT, title_color=RED))

story.append(PageBreak())

# ══════════════════════════════════════════════════════════════════════════════
# SECTION 6: FOLLOW-UP AND MONITORING
# ══════════════════════════════════════════════════════════════════════════════
story.append(section_header("6. FOLLOW-UP, MONITORING AND PATIENT EDUCATION", GREY_DARK))
story.append(Spacer(1, 0.35*cm))

story.append(Paragraph("Follow-up Schedule", h2_style))
fu_data = [
    ["Severity", "Initial Review", "Subsequent Follow-up"],
    ["Mild (Grade 1)", "4-6 weeks", "Every 3-6 months; earlier if worsening"],
    ["Moderate (Grade 2)", "2-4 weeks after starting Csa/Lifitegrast", "Every 2-3 months; titrate treatment"],
    ["Severe (Grade 3-4)", "1-2 weeks initially; after procedures", "Monthly until stable; 3-monthly long-term"],
    ["Post-Refractive Surgery", "1 week, 1 month, 3 months, 6 months", "As clinically indicated"],
]
fu_table = Table(fu_data, colWidths=[4*cm, 4*cm, 7*cm])
fu_table.setStyle(TableStyle([
    ('BACKGROUND', (0,0), (-1,0), GREY_DARK),
    ('TEXTCOLOR',  (0,0), (-1,0), white),
    ('FONTNAME',   (0,0), (-1,0), 'Helvetica-Bold'),
    ('FONTSIZE',   (0,0), (-1,0), 9),
    ('ALIGN',      (0,0), (-1,0), 'CENTER'),
    ('ALIGN',      (0,1), (0,-1), 'CENTER'),
    ('ALIGN',      (1,1), (-1,-1), 'LEFT'),
    ('VALIGN',     (0,0), (-1,-1), 'MIDDLE'),
    ('FONTNAME',   (0,1), (-1,-1), 'Helvetica'),
    ('FONTSIZE',   (0,1), (-1,-1), 8.5),
    ('ROWBACKGROUNDS', (0,1), (-1,-1), [white, GREY_LIGHT]),
    ('GRID',       (0,0), (-1,-1), 0.5, GREY_MID),
    ('ROWPADDING', (0,0), (-1,-1), [6, 4, 6, 4]),
]))
story.append(fu_table)
story.append(Spacer(1, 0.25*cm))

story.append(Paragraph("Treatment Response Assessment", h2_style))
tr_items = [
    "OSDI or SPEED score reduction (aim for >25% improvement)",
    "TBUT improvement (target > 10 sec non-invasive)",
    "Reduction in corneal/conjunctival staining grade",
    "Tear osmolarity normalization (< 308 mOsm/L or inter-eye difference < 8 mOsm/L)",
    "MMP-9 negativity on InflammaDry",
    "Schirmer improvement (in ADDE)",
    "Patient-reported improvement in daily activities and QoL",
]
for i in tr_items:
    story.append(Paragraph(f"• {i}", bullet_style))
story.append(Spacer(1, 0.2*cm))

story.append(Paragraph("Key Patient Education Points", h2_style))
edu_items = [
    "DED is a chronic condition requiring long-term management; not a 'quick fix'",
    "Compliance with regular instillation is essential even when asymptomatic",
    "Artificial tears wash away natural tears; use preservative-free if > 4 times/day",
    "Cyclosporine and lifitegrast require weeks-to-months to show full benefit",
    "Blink consciously during prolonged screen/reading time; take 20-20-20 breaks",
    "Avoid rubbing eyes (exacerbates inflammation and surface damage)",
    "Refrigerating preservative-free unit doses does not extend shelf life beyond 24h once opened",
    "Lubricating ointment use at bedtime for 3-6 months after epithelial healing reduces recurrence",
]
for i in edu_items:
    story.append(Paragraph(f"• {i}", bullet_style))

story.append(PageBreak())

# ══════════════════════════════════════════════════════════════════════════════
# SECTION 7: REFERENCES
# ══════════════════════════════════════════════════════════════════════════════
story.append(section_header("7. REFERENCES", GREY_DARK))
story.append(Spacer(1, 0.35*cm))

refs = [
    ("1", "TFOS DEWS II Executive Summary.",
     "Craig JP, Nelson JD, Azar DT, et al.",
     "Ocul Surf. 2017;15(4):802-812. DOI: 10.1016/j.jtos.2017.08.003"),
    ("2", "TFOS DEWS II Definition and Classification Report.",
     "Stapleton F, Alves M, Bunya VY, et al.",
     "Ocul Surf. 2017;15(3):276-283."),
    ("3", "TFOS DEWS II Pathophysiology Report.",
     "Bron AJ, de Paiva CS, Chauhan SK, et al.",
     "Ocul Surf. 2017;15(3):438-510."),
    ("4", "TFOS DEWS II Diagnostic Methodology Report.",
     "Wolffsohn JS, Arita R, Chalmers R, et al.",
     "Ocul Surf. 2017;15(3):539-574."),
    ("5", "TFOS DEWS II Management and Therapy Report.",
     "Jones L, Downie LE, Korb D, et al.",
     "Ocul Surf. 2017;15(3):575-628."),
    ("6", "Kanski's Clinical Ophthalmology: A Systematic Approach, 10th Edition.",
     "Bowling B.",
     "Elsevier, 2024. Chapter 5: Corneal and External Eye Disease."),
    ("7", "The Wills Eye Manual: Office and Emergency Room Diagnosis and Treatment, 8th Edition.",
     "Wills Eye Hospital.",
     "Wolters Kluwer, 2022. Section 4.3: Dry Eye Syndrome, pp. 171-176."),
    ("8", "Goodman & Gilman's The Pharmacological Basis of Therapeutics, 14th Edition.",
     "Brunton LL, et al. (Eds.).",
     "McGraw-Hill, 2023. Chapter on Ophthalmic Pharmacology."),
    ("9", "Association between potential factors and dry eye disease: A systematic review and meta-analysis.",
     "Yang K, Wu S, Ke L.",
     "Medicine (Baltimore). 2024 Dec 27. PMID: 39969375."),
]

ref_data = [["No.", "Title", "Authors", "Source"]]
for no, title, authors, source in refs:
    ref_data.append([no, title, authors, source])

ref_table = Table(ref_data, colWidths=[0.6*cm, 5.5*cm, 4.5*cm, 4.4*cm])
ref_table.setStyle(TableStyle([
    ('BACKGROUND', (0,0), (-1,0), GREY_DARK),
    ('TEXTCOLOR',  (0,0), (-1,0), 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), 7.5),
    ('ROWBACKGROUNDS', (0,1), (-1,-1), [white, GREY_LIGHT]),
    ('GRID',       (0,0), (-1,-1), 0.3, GREY_MID),
    ('ROWPADDING', (0,0), (-1,-1), [4, 3, 4, 3]),
]))
story.append(ref_table)

story.append(Spacer(1, 0.5*cm))
story.append(HRFlowable(width="100%", thickness=1, color=GREY_MID))
story.append(Spacer(1, 0.2*cm))
story.append(Paragraph(
    "This document is a clinical reference for educational purposes. "
    "All management decisions should be individualized based on clinical assessment. "
    "For the full TFOS DEWS II reports, visit: www.tearfilm.org",
    ref_style))

# ─── BUILD ────────────────────────────────────────────────────────────────────
doc.build(story)
print(f"PDF generated: {OUTPUT}")
Running Command

cd /tmp/workspace/dry-eye-pdf && python generate_dry_eye_pdf.py 2>&1

Running Command

ls -lh /tmp/workspace/dry-eye-pdf/Dry_Eye_Disease_Clinical_Reference.pdf

Running Command

python3 -c "from pypdf import PdfReader; r = PdfReader('/tmp/workspace/dry-eye-pdf/Dry_Eye_Disease_Clinical_Reference.pdf'); print(f'Pages: {len(r.pages)}')"

Your PDF is ready - a 9-page clinical reference document on Dry Eye Disease:

Dry Eye Disease Clinical Reference

PDF Document · PDF


Here's what the document covers across 9 pages:
Page 1 - Cover Titled "Dry Eye Disease - A Comprehensive Clinical Reference" with TFOS DEWS II attribution.
Section 1 - Definition and Overview
  • TFOS DEWS II 2017 official definition (multifactorial disease, loss of homeostasis, tear film instability, hyperosmolarity, inflammation, neurosensory abnormalities)
  • Tear film trilaminar structure table (lipid / aqueous / mucin layers - source, composition, function)
  • Pathophysiology and the vicious cycle (4 core mechanisms)
Section 2 - Classification (DEWS / DEWS II)
  • Two-column layout: Aqueous-Deficient DED vs Evaporative DED with full sub-classification
  • Severity grading table (Grade 1-4 with TBUT, Schirmer, staining, symptoms)
Section 3 - Evaluation and Diagnosis
  • Validated questionnaires (OSDI, SPEED, DEQ-5)
  • 8-step recommended test sequence table (TMH, TBUT, staining, osmolarity, MMP-9, Schirmer, meibography, lid wiper epitheliopathy) with method and cutoffs
  • Ancillary tests (IVCM, OCT, Sjögren workup)
Section 4 - Stepwise Management (DEWS II Levels 1-4)
  • Color-coded level boxes: Level 1 (Education/Environment), Level 2 (Anti-inflammatory), Level 3 (Advanced/Autologous serum/Scleral lens), Level 4 (Systemic/Surgical)
  • Wills Eye Manual severity-based quick reference table
Section 5 - Special Situations
  • Sjögren syndrome, MGD-predominant EDE, post-LASIK DED, filamentary keratitis
  • Drug classes that cause/worsen dry eye
Section 6 - Follow-up and Monitoring
  • Follow-up schedule by severity grade
  • Treatment response parameters
  • Patient education key points
Section 7 - References Full citations for TFOS DEWS II reports (5 sub-reports), Kanski's 10th ed., Wills Eye Manual 8th ed., Goodman & Gilman, and a 2024 meta-analysis (PMID 39969375).

Make the pdf on white background

Reading File
Writing File

~/dry-eye-pdf/generate_dry_eye_white.py

from reportlab.lib.pagesizes import A4
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.units import cm, mm
from reportlab.lib.colors import HexColor, white, black
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 BaseDocTemplate, Frame, PageTemplate
from reportlab.lib import colors

# ─── COLOR PALETTE (white background, accent colors only for borders/text) ───
DARK_BLUE   = HexColor("#0D3B66")
MID_BLUE    = HexColor("#1565C0")
TEAL        = HexColor("#00838F")
ORANGE      = HexColor("#E65100")
GREY_DARK   = HexColor("#263238")
GREY_MID    = HexColor("#607D8B")
GREY_LIGHT  = HexColor("#F5F5F5")
GREEN       = HexColor("#2E7D32")
RED         = HexColor("#C62828")
PURPLE      = HexColor("#6A1B9A")

# Very light tints (nearly white) - used sparingly for table row alternation only
ROW_ALT     = HexColor("#FAFAFA")

PAGE_W, PAGE_H = A4

# ─── STYLES ──────────────────────────────────────────────────────────────────
styles = getSampleStyleSheet()

def make_style(name, parent='Normal', **kwargs):
    return ParagraphStyle(name, parent=styles[parent], **kwargs)

# Cover styles (dark text on white cover)
cover_title_style = make_style('CoverTitle',
    fontSize=30, leading=36, textColor=DARK_BLUE,
    fontName='Helvetica-Bold', alignment=TA_CENTER, spaceAfter=6)

cover_sub_style = make_style('CoverSub',
    fontSize=14, leading=20, textColor=GREY_DARK,
    fontName='Helvetica', alignment=TA_CENTER, spaceAfter=4)

cover_tagline_style = make_style('CoverTagline',
    fontSize=11, leading=16, textColor=GREY_MID,
    fontName='Helvetica-Oblique', alignment=TA_CENTER, spaceAfter=2)

cover_date_style = make_style('CoverDate',
    fontSize=10, leading=14, textColor=GREY_MID,
    fontName='Helvetica', alignment=TA_CENTER)

# Section heading style (white text on colored banner — small left-bar variant)
h1_inner_style = make_style('H1Inner',
    fontSize=14, leading=19, textColor=white,
    fontName='Helvetica-Bold', alignment=TA_LEFT,
    spaceBefore=0, spaceAfter=0)

h2_style = make_style('H2',
    fontSize=12, leading=17, textColor=DARK_BLUE,
    fontName='Helvetica-Bold', alignment=TA_LEFT,
    spaceBefore=10, spaceAfter=3)

h3_style = make_style('H3',
    fontSize=10.5, leading=15, textColor=TEAL,
    fontName='Helvetica-Bold', alignment=TA_LEFT,
    spaceBefore=6, spaceAfter=2)

body_style = make_style('BodyText2',
    fontSize=9.5, leading=14, textColor=GREY_DARK,
    fontName='Helvetica', alignment=TA_JUSTIFY,
    spaceBefore=2, spaceAfter=2)

bullet_style = make_style('BulletText',
    fontSize=9.5, leading=14, textColor=GREY_DARK,
    fontName='Helvetica', alignment=TA_LEFT,
    leftIndent=14, firstLineIndent=-10,
    spaceBefore=1, spaceAfter=1)

sub_bullet_style = make_style('SubBullet',
    fontSize=9, leading=13, textColor=GREY_DARK,
    fontName='Helvetica', alignment=TA_LEFT,
    leftIndent=28, firstLineIndent=-10,
    spaceBefore=1, spaceAfter=1)

tip_style = make_style('TipText',
    fontSize=9.5, leading=14, textColor=GREY_DARK,
    fontName='Helvetica-Oblique', alignment=TA_LEFT,
    leftIndent=12, rightIndent=8,
    spaceBefore=4, spaceAfter=4)

ref_style = make_style('RefText',
    fontSize=8, leading=11, textColor=GREY_MID,
    fontName='Helvetica-Oblique', alignment=TA_LEFT,
    spaceBefore=1, spaceAfter=1)

# ─── HELPERS ─────────────────────────────────────────────────────────────────

def section_header(text, color=DARK_BLUE):
    """Colored banner (same as before but text on solid color strip)."""
    data = [[Paragraph(text, h1_inner_style)]]
    t = Table(data, colWidths=[PAGE_W - 4*cm])
    t.setStyle(TableStyle([
        ('BACKGROUND', (0,0), (-1,-1), color),
        ('TOPPADDING',    (0,0), (-1,-1), 7),
        ('BOTTOMPADDING', (0,0), (-1,-1), 7),
        ('LEFTPADDING',   (0,0), (-1,-1), 10),
        ('RIGHTPADDING',  (0,0), (-1,-1), 10),
    ]))
    return t


def info_box(title, items, border_color=MID_BLUE, bullet='•'):
    """White box with colored left border and colored title — NO fill."""
    title_p = Paragraph(f'<b>{title}</b>', make_style(f'IB_{title[:8]}',
        fontSize=10, leading=14, textColor=border_color,
        fontName='Helvetica-Bold', spaceBefore=0, spaceAfter=4))
    content = [title_p]
    for item in items:
        content.append(Paragraph(f'{bullet} {item}', make_style(f'IBB_{title[:6]}',
            fontSize=9, leading=13, textColor=GREY_DARK,
            fontName='Helvetica', leftIndent=8, firstLineIndent=0,
            spaceBefore=1, spaceAfter=1)))
    data = [[content]]
    col_w = PAGE_W - 4*cm
    t = Table(data, colWidths=[col_w])
    t.setStyle(TableStyle([
        ('BACKGROUND',    (0,0), (-1,-1), white),
        ('TOPPADDING',    (0,0), (-1,-1), 8),
        ('BOTTOMPADDING', (0,0), (-1,-1), 8),
        ('LEFTPADDING',   (0,0), (-1,-1), 12),
        ('RIGHTPADDING',  (0,0), (-1,-1), 10),
        ('BOX',           (0,0), (-1,-1), 0.75, GREY_MID),
        ('LINEBEFORE',    (0,0), (0,-1),  4,    border_color),
    ]))
    return t


def two_col_table(left_items, right_items, left_title, right_title,
                  left_tc=MID_BLUE, right_tc=TEAL):
    def make_cell(title, items, tc):
        cell = [Paragraph(f'<b>{title}</b>', make_style(f'CT_{title[:6]}',
            fontSize=10, leading=14, textColor=tc,
            fontName='Helvetica-Bold', spaceBefore=0, spaceAfter=5))]
        for i in items:
            if i == "":
                cell.append(Spacer(1, 4))
            else:
                prefix = '  ' if i.startswith('   ') else '• '
                cell.append(Paragraph(f'{prefix}{i.strip()}', make_style(f'CB_{title[:4]}',
                    fontSize=9, leading=13, textColor=GREY_DARK,
                    fontName='Helvetica', leftIndent=8, spaceBefore=1, spaceAfter=1)))
        return cell
    data = [[make_cell(left_title, left_items, left_tc),
             make_cell(right_title, right_items, right_tc)]]
    col_w = (PAGE_W - 4*cm - 0.5*cm) / 2
    t = Table(data, colWidths=[col_w, col_w], hAlign='LEFT')
    t.setStyle(TableStyle([
        ('BACKGROUND',    (0,0), (-1,-1), white),
        ('VALIGN',        (0,0), (-1,-1), 'TOP'),
        ('TOPPADDING',    (0,0), (-1,-1), 8),
        ('BOTTOMPADDING', (0,0), (-1,-1), 8),
        ('LEFTPADDING',   (0,0), (-1,-1), 10),
        ('RIGHTPADDING',  (0,0), (-1,-1), 10),
        ('BOX',           (0,0), (0,-1),  1, left_tc),
        ('BOX',           (1,0), (1,-1),  1, right_tc),
        ('LINEBEFORE',    (0,0), (0,-1),  4, left_tc),
        ('LINEBEFORE',    (1,0), (1,-1),  4, right_tc),
    ]))
    return t


def level_box(level_num, title, items, color_map):
    color = color_map.get(level_num, GREY_MID)
    label = Paragraph(f'<b>LEVEL {level_num}</b>', make_style(f'LLbl{level_num}',
        fontSize=18, leading=22, textColor=color,
        fontName='Helvetica-Bold', alignment=TA_CENTER))
    sub = Paragraph(title, make_style(f'LSub{level_num}',
        fontSize=8.5, leading=12, textColor=GREY_DARK,
        fontName='Helvetica-Bold', alignment=TA_CENTER))
    bullets = []
    for i in items:
        if i.startswith('   '):
            bullets.append(Paragraph(f'    {i.strip()}', make_style(f'LSB{level_num}',
                fontSize=8.5, leading=12, textColor=GREY_MID,
                fontName='Helvetica', leftIndent=16, spaceBefore=0, spaceAfter=1)))
        else:
            bullets.append(Paragraph(f'• {i}', make_style(f'LB{level_num}',
                fontSize=9, leading=13, textColor=GREY_DARK,
                fontName='Helvetica', leftIndent=8, spaceBefore=1, spaceAfter=1)))
    left_cell  = [label, Spacer(1, 6), sub]
    right_cell = bullets
    col_w_l = 3.2*cm
    col_w_r = (PAGE_W - 4*cm) - col_w_l - 0.3*cm
    data = [[left_cell, right_cell]]
    t = Table(data, colWidths=[col_w_l, col_w_r])
    t.setStyle(TableStyle([
        ('BACKGROUND',    (0,0), (-1,-1), white),
        ('VALIGN',        (0,0), (0,-1),  'MIDDLE'),
        ('VALIGN',        (1,0), (1,-1),  'TOP'),
        ('TOPPADDING',    (0,0), (-1,-1), 8),
        ('BOTTOMPADDING', (0,0), (-1,-1), 8),
        ('LEFTPADDING',   (0,0), (0,-1),  6),
        ('LEFTPADDING',   (1,0), (1,-1),  12),
        ('BOX',           (0,0), (-1,-1), 1.5, color),
        ('LINEAFTER',     (0,0), (0,-1),  1,   color),
        ('LINEBEFORE',    (0,0), (0,-1),  4,   color),
    ]))
    return t


# ─── PAGE DECORATIONS ─────────────────────────────────────────────────────────
def add_page_decorations(canvas, doc):
    canvas.saveState()
    # Thin top rule
    canvas.setFillColor(DARK_BLUE)
    canvas.rect(0, PAGE_H - 5*mm, PAGE_W, 5*mm, fill=1, stroke=0)
    # Bottom thin rule
    canvas.setFillColor(GREY_LIGHT)
    canvas.rect(0, 0, PAGE_W, 1.2*cm, fill=1, stroke=0)
    canvas.setStrokeColor(GREY_MID)
    canvas.setLineWidth(0.5)
    canvas.line(0, 1.2*cm, PAGE_W, 1.2*cm)
    canvas.setFillColor(GREY_MID)
    canvas.setFont('Helvetica', 7.5)
    canvas.drawCentredString(PAGE_W/2, 0.45*cm,
        f'Dry Eye Disease — Clinical Reference | TFOS DEWS II Framework | Page {doc.page}')
    canvas.restoreState()


def build_cover(canvas, doc):
    """White cover with a dark blue top banner and blue bottom stripe."""
    canvas.saveState()
    # Full white background
    canvas.setFillColor(white)
    canvas.rect(0, 0, PAGE_W, PAGE_H, fill=1, stroke=0)
    # Top banner (about 1/3 of page)
    canvas.setFillColor(DARK_BLUE)
    canvas.rect(0, PAGE_H * 0.68, PAGE_W, PAGE_H * 0.32, fill=1, stroke=0)
    # Thin accent line below banner
    canvas.setFillColor(TEAL)
    canvas.rect(0, PAGE_H * 0.67, PAGE_W, PAGE_H * 0.012, fill=1, stroke=0)
    # Bottom thin stripe
    canvas.setFillColor(GREY_LIGHT)
    canvas.rect(0, 0, PAGE_W, 1.5*cm, fill=1, stroke=0)
    canvas.setFillColor(DARK_BLUE)
    canvas.rect(0, 1.4*cm, PAGE_W, 2*mm, fill=1, stroke=0)
    canvas.restoreState()


# ─── COVER TEXT STYLES (used directly on canvas for cover) ──────────────────
cover_banner_title = make_style('CBT',
    fontSize=30, leading=36, textColor=white,
    fontName='Helvetica-Bold', alignment=TA_CENTER, spaceAfter=6)

cover_banner_sub = make_style('CBS',
    fontSize=13, leading=18, textColor=HexColor("#B3E5FC"),
    fontName='Helvetica', alignment=TA_CENTER, spaceAfter=4)

cover_body_title = make_style('CBTB',
    fontSize=18, leading=24, textColor=DARK_BLUE,
    fontName='Helvetica-Bold', alignment=TA_CENTER, spaceAfter=6)

cover_body_sub = make_style('CBSB',
    fontSize=11, leading=16, textColor=GREY_DARK,
    fontName='Helvetica', alignment=TA_CENTER, spaceAfter=4)

cover_ref_style = make_style('CBREF',
    fontSize=9, leading=13, textColor=GREY_MID,
    fontName='Helvetica-Oblique', alignment=TA_CENTER, spaceAfter=2)


# ─── BUILD THE PDF ────────────────────────────────────────────────────────────
OUTPUT = '/tmp/workspace/dry-eye-pdf/Dry_Eye_Disease_Clinical_Reference.pdf'

doc = BaseDocTemplate(
    OUTPUT,
    pagesize=A4,
    rightMargin=2*cm, leftMargin=2*cm,
    topMargin=1.5*cm, bottomMargin=1.8*cm,
)

cover_frame  = Frame(2*cm, 1.8*cm, PAGE_W - 4*cm, PAGE_H - 3.3*cm, id='cover')
content_frame = Frame(2*cm, 1.8*cm, PAGE_W - 4*cm, PAGE_H - 3.3*cm, id='content')

cover_template = PageTemplate(id='Cover', frames=[cover_frame], onPage=build_cover)
main_template  = PageTemplate(id='Main',  frames=[content_frame], onPage=add_page_decorations)
doc.addPageTemplates([cover_template, main_template])

story = []

# ══════════════════════════════════════════════════════════════════════════════
# COVER PAGE
# ══════════════════════════════════════════════════════════════════════════════
# Content that sits inside the blue top banner
story.append(Spacer(1, 3.2*cm))   # push into banner zone
story.append(Paragraph("DRY EYE DISEASE", cover_banner_title))
story.append(Spacer(1, 0.4*cm))
story.append(Paragraph("A Comprehensive Clinical Reference", cover_banner_sub))
story.append(Spacer(1, 0.2*cm))
story.append(Paragraph(
    "Definition · Classification · Evaluation · Stepwise Management",
    cover_banner_sub))

# Content in white zone
story.append(Spacer(1, 2.2*cm))
story.append(HRFlowable(width="60%", thickness=2, color=TEAL, hAlign='CENTER'))
story.append(Spacer(1, 0.5*cm))
story.append(Paragraph(
    "Based on the TFOS DEWS II International Dry Eye Workshop (2017) Guidelines",
    cover_body_title))
story.append(Spacer(1, 0.4*cm))
story.append(Paragraph(
    "Tear Film &amp; Ocular Surface Society Dry Eye Workshop II",
    cover_body_sub))
story.append(Spacer(1, 0.3*cm))
story.append(HRFlowable(width="40%", thickness=0.75, color=GREY_MID, hAlign='CENTER'))
story.append(Spacer(1, 0.4*cm))
story.append(Paragraph(
    "References: Kanski's Clinical Ophthalmology (10th Ed.) · "
    "Wills Eye Manual (8th Ed.) · TFOS DEWS II Reports (Ocul Surf. 2017)",
    cover_ref_style))
story.append(Spacer(1, 0.2*cm))
story.append(Paragraph("July 2026", cover_ref_style))

story.append(PageBreak())

# ══════════════════════════════════════════════════════════════════════════════
# SECTION 1: DEFINITION & OVERVIEW
# ══════════════════════════════════════════════════════════════════════════════
story.append(section_header("1. DEFINITION AND OVERVIEW", DARK_BLUE))
story.append(Spacer(1, 0.35*cm))

story.append(Paragraph("TFOS DEWS II Definition (2017)", h2_style))
story.append(Paragraph(
    "Dry eye is a <b>multifactorial disease of the ocular surface</b> characterised by a loss of "
    "homeostasis of the tear film, and accompanied by ocular symptoms, in which "
    "<b>tear film instability and hyperosmolarity, ocular surface inflammation and damage, "
    "and neurosensory abnormalities</b> play etiological roles.",
    body_style))
story.append(Spacer(1, 0.2*cm))

story.append(info_box(
    "Key Conceptual Pillars of DED (DEWS II)",
    [
        "Loss of tear film HOMEOSTASIS is the central unifying concept",
        "Ocular symptoms must be present (distinguishes from asymptomatic tear dysfunction)",
        "Tear film instability → hyperosmolarity → inflammation → ocular surface damage",
        "Neurosensory abnormalities recognized as etiological contributors",
        "Positive feedback ('vicious cycle') perpetuates and amplifies the disease",
    ],
    border_color=DARK_BLUE))
story.append(Spacer(1, 0.3*cm))

story.append(Paragraph("Tear Film Structure and Function", h2_style))
story.append(Paragraph(
    "The tear film is a trilaminar fluid layer covering the ocular surface:",
    body_style))
story.append(Spacer(1, 0.15*cm))

tear_data = [
    ["Layer", "Source", "Composition", "Function"],
    ["Lipid (Outer)", "Meibomian glands", "Phospholipids, triglycerides,\ncholesterol esters, wax esters",
     "Retards evaporation; stabilizes\ntear film; smooths optical surface"],
    ["Aqueous (Middle)", "Main & accessory\nlacrimal glands", "Water, electrolytes, proteins\n(lactoferrin, lysozyme, IgA)",
     "Nourishes cornea; dilutes irritants;\nantimicrobial; oxygen supply"],
    ["Mucin (Inner)", "Goblet cells;\ncorneal epithelium",
     "Transmembrane mucins\n(MUC1, 4, 16); secreted MUC5AC",
     "Hydrophilic conversion of surface;\nfacilitates wetting & lubrication"],
]
tear_table = Table(tear_data, colWidths=[2.8*cm, 3.2*cm, 4.5*cm, 4.5*cm])
tear_table.setStyle(TableStyle([
    ('BACKGROUND', (0,0), (-1,0), DARK_BLUE),
    ('TEXTCOLOR',  (0,0), (-1,0), white),
    ('FONTNAME',   (0,0), (-1,0), 'Helvetica-Bold'),
    ('FONTSIZE',   (0,0), (-1,0), 9),
    ('BACKGROUND', (0,1), (-1,-1), white),
    ('ROWBACKGROUNDS', (0,1), (-1,-1), [white, ROW_ALT]),
    ('ALIGN',      (0,0), (-1,-1), 'LEFT'),
    ('FONTNAME',   (0,1), (-1,-1), 'Helvetica'),
    ('FONTSIZE',   (0,1), (-1,-1), 8.5),
    ('GRID',       (0,0), (-1,-1), 0.5, GREY_MID),
    ('ROWPADDING', (0,0), (-1,-1), [5, 4, 5, 4]),
    ('VALIGN',     (0,0), (-1,-1), 'TOP'),
]))
story.append(tear_table)
story.append(Spacer(1, 0.25*cm))

story.append(Paragraph("Pathophysiology — The Vicious Cycle", h2_style))
story.append(Paragraph("The four core inter-related mechanisms:", body_style))
story.append(Spacer(1, 0.1*cm))
path_items = [
    ("Tear Instability", "Reduced TBUT; loss of surface mucins; meibomian gland dysfunction"),
    ("Tear Hyperosmolarity", "Core mechanism; damages epithelial cells directly; triggers inflammatory "
     "cascades; threshold ~308 mOsm/L; >316 mOsm/L = moderate-severe"),
    ("Ocular Surface Inflammation", "Present in 80% of KCS; both cause and consequence; "
     "T-cell activation, IL-1, IL-6, TNF-α, MMP-9 upregulation"),
    ("Ocular Surface Damage", "Punctate epithelial erosions, mucous plaques, corneal filaments, "
     "goblet cell loss; perpetuates the vicious cycle"),
]
for title, detail in path_items:
    story.append(Paragraph(f'<b>• {title}:</b> {detail}', bullet_style))

story.append(Spacer(1, 0.2*cm))
story.append(info_box(
    "Clinical Tips (Kanski's)",
    ["Whether a patient has primary aqueous deficiency or evaporative dry eye, "
     "the end result is ocular surface inflammation and discomfort.",
     "There is a poor correlation between symptoms and clinical tests; "
     "reliability improves as severity increases."],
    border_color=ORANGE))

story.append(PageBreak())

# ══════════════════════════════════════════════════════════════════════════════
# SECTION 2: CLASSIFICATION
# ══════════════════════════════════════════════════════════════════════════════
story.append(section_header("2. CLASSIFICATION (DEWS / DEWS II)", MID_BLUE))
story.append(Spacer(1, 0.35*cm))
story.append(Paragraph(
    "The DEWS (2007) and DEWS II (2017) classification divides dry eye into two primary "
    "etiological subtypes. Most patients present with significant overlap between mechanisms.",
    body_style))
story.append(Spacer(1, 0.25*cm))

story.append(two_col_table(
    left_title="I. AQUEOUS-DEFICIENT DRY EYE (ADDE)",
    left_items=[
        "Sjögren Syndrome (SS) DED",
        "   Primary SS (isolated)",
        "   Secondary SS (RA, SLE, PSC)",
        "",
        "Non-Sjögren Syndrome DED:",
        "Lacrimal deficiency — Primary:",
        "   Age-related dry eye",
        "   Congenital alacrima",
        "   Familial dysautonomia (Riley-Day)",
        "Lacrimal deficiency — Secondary:",
        "   Inflammatory/neoplastic infiltration",
        "   AIDS, Graft-versus-host disease",
        "   Lacrimal gland/nerve ablation",
        "Lacrimal duct obstruction:",
        "   Trachoma, cicatricial pemphigoid",
        "   Chemical burns, Stevens-Johnson",
        "Reflex hyposecretion — Sensory:",
        "   Contact lens wear, diabetes",
        "   Refractive surgery, neurotrophic keratitis",
        "Reflex hyposecretion — Motor:",
        "   CN VII palsy, systemic drugs",
    ],
    right_title="II. EVAPORATIVE DRY EYE (EDE)",
    right_items=[
        "A. INTRINSIC CAUSES:",
        "Meibomian Gland Dysfunction (MGD):",
        "   Posterior blepharitis, rosacea",
        "Lid aperture disorders:",
        "   Scleral show, lid retraction",
        "   Proptosis, facial nerve palsy",
        "Low blink rate:",
        "   Parkinson disease",
        "   VDU/computer/reading use",
        "Drug-induced evaporation:",
        "   Antihistamines, beta-blockers",
        "   Antispasmodics, diuretics",
        "",
        "B. EXTRINSIC CAUSES:",
        "Vitamin A deficiency",
        "Topical drug toxicity / preservatives",
        "Contact lens wear",
        "Ocular surface disease (allergic conjunctivitis)",
    ],
    left_tc=MID_BLUE, right_tc=TEAL))

story.append(Spacer(1, 0.3*cm))
story.append(Paragraph("DEWS II Severity Grading", h2_style))
story.append(Paragraph(
    "Severity is graded 1–4 using the OSDI questionnaire combined with clinical findings. "
    "Treatment advances stepwise through these levels.",
    body_style))
story.append(Spacer(1, 0.15*cm))

sev_data = [
    ["Grade", "Symptoms", "Conjunctival Staining", "Corneal Staining", "TBUT (sec)", "Schirmer (mm/5min)"],
    ["1 — Mild",    "Mild/episodic\nenvironmental triggers", "None–mild",      "None",               ">10",  ">10"],
    ["2 — Moderate","Moderate; episodic\nor chronic",         "None–mild",      "None–mild peripheral","<10",  ">10 or borderline"],
    ["3 — Severe",  "Frequent/chronic;\nactivity limitation", "Moderate–severe","Moderate central",    "<5",   "<5"],
    ["4 — V. Severe","Constant; disabling;\nno relief",       "Severe + corneal","Severe (filaments)", "Immed.","<2"],
]
sev_table = Table(sev_data, colWidths=[2.2*cm, 3.8*cm, 3.2*cm, 3.0*cm, 1.6*cm, 1.2*cm])
sev_table.setStyle(TableStyle([
    ('BACKGROUND', (0,0), (-1,0), DARK_BLUE),
    ('TEXTCOLOR',  (0,0), (-1,0), white),
    ('FONTNAME',   (0,0), (-1,0), 'Helvetica-Bold'),
    ('FONTSIZE',   (0,0), (-1,0), 8.5),
    ('BACKGROUND', (0,1), (-1,-1), white),
    ('ROWBACKGROUNDS', (0,1), (-1,-1), [white, ROW_ALT]),
    ('ALIGN',      (0,0), (-1,-1), 'CENTER'),
    ('VALIGN',     (0,0), (-1,-1), 'MIDDLE'),
    ('FONTNAME',   (0,1), (-1,-1), 'Helvetica'),
    ('FONTSIZE',   (0,1), (-1,-1), 8),
    ('GRID',       (0,0), (-1,-1), 0.5, GREY_MID),
    ('ROWPADDING', (0,0), (-1,-1), [5, 4, 5, 4]),
    # Colored left cells
    ('TEXTCOLOR',  (0,1), (0,1), GREEN),
    ('TEXTCOLOR',  (0,2), (0,2), MID_BLUE),
    ('TEXTCOLOR',  (0,3), (0,3), ORANGE),
    ('TEXTCOLOR',  (0,4), (0,4), RED),
    ('FONTNAME',   (0,1), (0,-1), 'Helvetica-Bold'),
]))
story.append(sev_table)

story.append(PageBreak())

# ══════════════════════════════════════════════════════════════════════════════
# SECTION 3: EVALUATION
# ══════════════════════════════════════════════════════════════════════════════
story.append(section_header("3. EVALUATION AND DIAGNOSIS", TEAL))
story.append(Spacer(1, 0.35*cm))
story.append(Paragraph(
    "A systematic approach integrates patient symptoms, clinical signs, and objective tests. "
    "DEWS II recommends a triaging approach: validated questionnaires → diagnostic tests "
    "in sequence to minimise confounding.",
    body_style))
story.append(Spacer(1, 0.25*cm))

story.append(Paragraph("3.1  Symptoms and History", h2_style))
story.append(Paragraph(
    "Characteristic symptoms: burning, dryness, grittiness, foreign body sensation, "
    "mildly decreased vision, and paradoxical excess tearing. Worsen with environmental "
    "triggers (wind, AC, smoke, low humidity) and prolonged visual tasks that reduce blink rate.",
    body_style))
story.append(Spacer(1, 0.1*cm))

story.append(info_box(
    "Validated Questionnaires",
    [
        "OSDI (Ocular Surface Disease Index) — 12 items; score 0–100; "
        ">12 = mild, >23 = moderate, >32 = severe DED",
        "SPEED (Standard Patient Evaluation of Eye Dryness)",
        "DEQ-5 (Dry Eye Questionnaire 5)",
        "SANDE (Symptom Assessment in Dry Eye)",
    ],
    border_color=MID_BLUE))
story.append(Spacer(1, 0.25*cm))

story.append(Paragraph("3.2  Recommended Test Sequence (Wills Eye / DEWS II)", h2_style))
story.append(Paragraph(
    "Perform in this order — Schirmer strips can damage the ocular surface and affect staining.",
    body_style))
story.append(Spacer(1, 0.15*cm))

workup_data = [
    ["Step", "Test", "Method / Interpretation", "Normal / Cutoff"],
    ["1", "Tear Meniscus\nHeight (TMH)",
     "Slit lamp before any drops; assess inferior lid margin height",
     "≥ 0.2 mm (DEWS II)\n≥ 0.5 mm (Wills Eye)"],
    ["2", "Tear Film Break-up\nTime (TBUT/NIKBUT)",
     "Fluorescein 2% + cobalt blue; time from last blink to first dry spot.\n"
     "Non-invasive TBUT (NIKBUT) preferred by DEWS II.",
     "< 10 sec = suspicious\n< 5 sec = severe\nNIKBUT < 10 sec = abnormal"],
    ["3", "Ocular Surface\nStaining",
     "Fluorescein: corneal/conjunctival epithelial damage.\n"
     "Lissamine green: devitalized cells (preferred; less stinging).\n"
     "Rose Bengal: dead/devitalized cells (more intense; stings).",
     "Oxford / van Bijsterveld grading.\nInterpalpebral = ADDE;\nInferior = blepharitis"],
    ["4", "Tear Osmolarity",
     "TearLab™ or similar; inter-eye difference >8 mOsm/L also significant.",
     "Normal ≤ 308 mOsm/L\n308–316 = mild–moderate\n>316 = moderate–severe"],
    ["5", "MMP-9\n(InflammaDry)",
     "Point-of-care test; detects ocular surface inflammation marker.",
     "≥ 40 ng/mL = positive\nSn 85%; Sp 94%"],
    ["6", "Schirmer Test",
     "No. 41 Whatman paper (5×35 mm); lower lid junction middle/lateral 1/3; 5 min.\n"
     "Schirmer I (without anaesthetic): basal + reflex secretion.\n"
     "Schirmer II (with proparacaine): basal secretion only.",
     "Schirmer I: ≥ 15 mm/5 min\nSchirmer II: ≥ 6 mm/5 min\n< 5 mm = severely abnormal"],
    ["7", "Meibomian Gland\nEvaluation",
     "Meibography (IR transillumination): gland dropout grading.\n"
     "Meibum expression: quality (clear → turbid → toothpaste).\n"
     "Lid margin: vascular engorgement, notching, orifice plugging.",
     "Meiboscore 0–3 per lid\n(3 = >67% gland dropout)"],
    ["8", "Lid Wiper\nEpitheliopathy",
     "Lissamine green staining of upper tarsal conjunctival wiper zone; friction marker.",
     "Staining ≥ 2 mm wide\nor ≥ 25% circumference\n= positive"],
]
workup_table = Table(workup_data, colWidths=[0.8*cm, 3.0*cm, 7.0*cm, 4.2*cm])
workup_table.setStyle(TableStyle([
    ('BACKGROUND', (0,0), (-1,0), TEAL),
    ('TEXTCOLOR',  (0,0), (-1,0), white),
    ('FONTNAME',   (0,0), (-1,0), 'Helvetica-Bold'),
    ('FONTSIZE',   (0,0), (-1,0), 9),
    ('BACKGROUND', (0,1), (-1,-1), white),
    ('ROWBACKGROUNDS', (0,1), (-1,-1), [white, ROW_ALT]),
    ('ALIGN',      (0,0), (0,-1), 'CENTER'),
    ('ALIGN',      (1,0), (-1,-1), 'LEFT'),
    ('VALIGN',     (0,0), (-1,-1), 'TOP'),
    ('FONTNAME',   (0,1), (-1,-1), 'Helvetica'),
    ('FONTSIZE',   (0,1), (-1,-1), 8.5),
    ('GRID',       (0,0), (-1,-1), 0.5, GREY_MID),
    ('ROWPADDING', (0,0), (-1,-1), [5, 4, 5, 4]),
    ('FONTNAME',   (0,1), (0,-1), 'Helvetica-Bold'),
    ('TEXTCOLOR',  (0,1), (0,-1), TEAL),
]))
story.append(workup_table)
story.append(Spacer(1, 0.25*cm))

story.append(Paragraph("3.3  Additional / Ancillary Investigations", h2_style))
additional_tests = [
    "Phenol red thread test: pH-sensitive thread; 15 sec; < 6 mm = abnormal",
    "Tear lactoferrin: Low levels suggest ADDE (Lactoferrin InflammaDry or ELISA)",
    "Fluorescein clearance test / Tear function index: Delayed clearance in all dry eye states",
    "Impression cytology: Goblet cell density; squamous metaplasia grading",
    "In-vivo confocal microscopy (IVCM): Corneal nerve density; sub-basal nerve plexus",
    "Anterior segment OCT: Quantitative tear meniscus height/volume",
    "Sjögren workup: Anti-SSA/SSB, ANA, RF, minor salivary gland biopsy",
]
for t in additional_tests:
    story.append(Paragraph(f"• {t}", bullet_style))

story.append(PageBreak())

# ══════════════════════════════════════════════════════════════════════════════
# SECTION 4: STEPWISE MANAGEMENT
# ══════════════════════════════════════════════════════════════════════════════
story.append(section_header("4. STEPWISE MANAGEMENT (DEWS II FRAMEWORK)", GREEN))
story.append(Spacer(1, 0.35*cm))
story.append(Paragraph(
    "The DEWS II guidelines provide a severity-based, stepwise treatment algorithm. "
    "Management proceeds to the next level if preceding measures are inadequate. "
    "Underlying processes are generally not reversible; treatment aims to "
    "control symptoms and prevent surface damage.",
    body_style))
story.append(Spacer(1, 0.15*cm))

level_colors = {
    1: GREEN,
    2: MID_BLUE,
    3: ORANGE,
    4: RED,
}

# Level 1
story.append(level_box(1, "EDUCATION, ENVIRONMENT & FIRST-LINE THERAPIES  (All patients — Grade 1)", [
    "Patient education: realistic expectations, importance of compliance",
    "Blink exercises: conscious blinking during reading, VDT use, TV viewing",
    "Screen positioning: below eye level to reduce palpebral aperture size",
    "Lifestyle modification: humidifiers, avoid fans/AC drafts, protect from wind",
    "Systemic medication review: identify and eliminate contributory drugs",
    "Discontinue toxic/preserved topical medications where possible",
    "Artificial tear substitutes (preserved drops q.i.d.; gels; ointments at HS)",
    "Basic eyelid therapy: warm compresses + lid hygiene for blepharitis/MGD",
    "Reparative lid surgery if needed: entropion, ectropion, lagophthalmos",
    "Caution: advise against laser refractive surgery in active DED",
    "Contact lenses: modify schedule; consider daily disposables or silicone hydrogel",
    "Dietary: Omega-3 supplementation (fish oil 1–3 g/day or flaxseed oil)",
], level_colors))
story.append(Spacer(1, 0.25*cm))

# Level 2
story.append(level_box(2, "ANTI-INFLAMMATORY AND SUPPLEMENTAL THERAPIES  (Grades 2–3)", [
    "Non-preserved artificial tears (up to q1–2h PRN)",
    "Lubricating gel or ointment q.h.s.",
    "CYCLOSPORINE A 0.05% or 0.09% (Restasis / Cequa) b.i.d.:",
    "   Indicated for chronic DED with decreased tears due to ocular inflammation",
    "   Burns on instillation for first weeks; onset 1–3 months",
    "   Consider concurrent mild topical steroid (loteprednol 0.5% or FML 0.1%) for 1 month",
    "LIFITEGRAST 5% (Xiidra) b.i.d.: LFA-1 integrin antagonist; anti-inflammatory",
    "   Symptomatic improvement within 2 weeks; full effect up to 3 months",
    "   May cause instillation burn, transient blur, metallic taste",
    "Topical steroids (short-course only): FML 0.1%, loteprednol 0.5%, prednisolone 1%",
    "   For acute exacerbations; monitor IOP; avoid prolonged use",
    "Tetracyclines: doxycycline 50–100 mg/day for MGD, rosacea-associated DED",
    "   Anti-MMP and anti-inflammatory properties; minimum 6–8 weeks",
    "Secretagogues: pilocarpine 5 mg QID, cevimeline 30 mg TID (for Sjögren syndrome)",
    "Rebamipide 2% QID: mucin secretagogue; improves goblet cell density",
    "Punctal plugs (collagen temporary or silicone/acrylic reversible):",
    "   Occlude lower punctum first; reduces tear drainage",
    "   Treat active inflammation/blepharitis BEFORE plugging",
    "Moisture chamber spectacles / wrap-around side shields",
], level_colors))
story.append(Spacer(1, 0.25*cm))

# Level 3
story.append(level_box(3, "ADVANCED THERAPIES FOR REFRACTORY DISEASE  (Grade 3 — Severe)", [
    "AUTOLOGOUS SERUM EYE DROPS (20–100% concentration):",
    "   Rich in growth factors (EGF, TGF-β), vitamins, fibronectin",
    "   Promotes epithelial healing; use q.i.d. to q2h; refrigerate; 3–6 month shelf life",
    "UMBILICAL CORD SERUM: similar to autologous; higher growth factor content",
    "SCLERAL CONTACT LENSES: fluid reservoir over cornea; excellent for severe/irregular surface",
    "Bandage soft contact lens: for erosions, filamentary keratitis",
    "Permanent punctal occlusion (thermal/argon laser cautery) if plugs repeatedly fall out",
    "   Occlude upper and lower puncta if needed",
    "FILAMENTARY KERATITIS management:",
    "   Remove filaments with jeweller's forceps at slit lamp",
    "   Acetylcysteine 10% q.i.d. (mucolytic)",
    "Topical vitamin A (retinol palmitate): promotes epithelial and goblet cell recovery",
    "Intense Pulsed Light (IPL): for rosacea-associated MGD/EDE",
    "LipiFlow thermal pulsation: vectored heat + pressure for meibomian gland obstruction",
], level_colors))
story.append(Spacer(1, 0.25*cm))

# Level 4
story.append(level_box(4, "SYSTEMIC AND SURGICAL INTERVENTIONS  (Grade 4 — Very Severe / Refractory)", [
    "Systemic anti-inflammatory agents (Sjögren, cicatricial diseases):",
    "   Hydroxychloroquine, azathioprine, mycophenolate mofetil, rituximab",
    "TARSORRHAPHY:",
    "   Temporary adhesive tape tarsorrhaphy (lateral 1/3 of lids, pending surgery)",
    "   Permanent lateral tarsorrhaphy for refractory exposure/evaporative DED",
    "AMNIOTIC MEMBRANE TRANSPLANTATION: for persistent epithelial defects",
    "SALIVARY GLAND TRANSPLANTATION: labial salivary gland grafts to conjunctiva",
    "CONJUNCTIVAL / LIMBAL TRANSPLANTATION: severe cicatricial disease",
    "MUCOUS MEMBRANE GRAFTING: cicatricial pemphigoid, Stevens-Johnson syndrome",
    "Meibomian gland intraductal probing: for refractory scarring/obstruction",
    "Botulinum toxin for punctal closure (experimental)",
], level_colors))

story.append(Spacer(1, 0.3*cm))
story.append(Paragraph("Summary: Wills Eye Manual Severity-Based Quick Guide", h2_style))

quick_data = [
    ["Severity", "Key Treatments"],
    ["MILD\n(Grade 1)",
     "Artificial tears q.i.d. (preserved acceptable) · Environmental/lifestyle modification · Lid hygiene if blepharitis"],
    ["MODERATE\n(Grade 2)",
     "Preservative-free tears up to q1–2h · Lubricating gel/ointment q.h.s. · "
     "Cyclosporine 0.05% or 0.09% b.i.d. · Lifitegrast 5% b.i.d. · "
     "Lifestyle modification · Punctal occlusion if above inadequate"],
    ["SEVERE\n(Grade 3–4)",
     "Continue cyclosporine/lifitegrast · Punctal occlusion (lower + upper; thermal cautery if plugs fail) · "
     "Lubricating gel b.i.d.–q.i.d. · Moisture chamber goggles · "
     "Acetylcysteine 10% for filamentary keratitis · Autologous serum · Topical vitamin A · "
     "Scleral/bandage lens · Lateral tarsorrhaphy as last resort"],
]
quick_table = Table(quick_data, colWidths=[2.8*cm, 12.2*cm])
quick_table.setStyle(TableStyle([
    ('BACKGROUND', (0,0), (-1,0), GREEN),
    ('TEXTCOLOR',  (0,0), (-1,0), white),
    ('FONTNAME',   (0,0), (-1,0), 'Helvetica-Bold'),
    ('FONTSIZE',   (0,0), (-1,0), 9.5),
    ('BACKGROUND', (0,1), (-1,-1), white),
    ('ROWBACKGROUNDS', (0,1), (-1,-1), [white, ROW_ALT]),
    ('ALIGN',      (0,0), (0,-1), 'CENTER'),
    ('ALIGN',      (1,1), (1,-1), 'LEFT'),
    ('VALIGN',     (0,0), (-1,-1), 'MIDDLE'),
    ('FONTNAME',   (0,1), (-1,-1), 'Helvetica'),
    ('FONTSIZE',   (0,1), (-1,-1), 8.5),
    ('GRID',       (0,0), (-1,-1), 0.5, GREY_MID),
    ('ROWPADDING', (0,0), (-1,-1), [6, 5, 6, 5]),
    ('FONTNAME',   (0,1), (0,-1), 'Helvetica-Bold'),
    ('TEXTCOLOR',  (0,1), (0,1), GREEN),
    ('TEXTCOLOR',  (0,2), (0,2), MID_BLUE),
    ('TEXTCOLOR',  (0,3), (0,3), RED),
]))
story.append(quick_table)

story.append(PageBreak())

# ══════════════════════════════════════════════════════════════════════════════
# SECTION 5: SPECIAL SITUATIONS
# ══════════════════════════════════════════════════════════════════════════════
story.append(section_header("5. SPECIAL SITUATIONS AND MANAGEMENT PEARLS", PURPLE))
story.append(Spacer(1, 0.35*cm))

story.append(Paragraph("5.1  Sjögren Syndrome DED", h2_style))
story.append(Paragraph(
    "SS DED is typically severe ADDE. ACR criteria: positive anti-SSA/SSB (or RF + ANA), "
    "ocular surface staining above threshold grade, and focal lymphocytic sialadenitis "
    "on minor salivary gland biopsy.",
    body_style))
for i in [
    "All topical therapies at appropriate severity level",
    "Cyclosporine 0.05% preferred given the strong inflammatory component",
    "Punctal occlusion often required early in disease course",
    "Secretagogues: pilocarpine 5 mg QID or cevimeline 30 mg TID for systemic dryness",
    "Hydroxychloroquine for systemic manifestations",
    "Rheumatology referral essential; monitor for lymphoma (increased risk in primary SS)",
]:
    story.append(Paragraph(f"• {i}", bullet_style))
story.append(Spacer(1, 0.2*cm))

story.append(Paragraph("5.2  MGD-Predominant / Evaporative DED", h2_style))
for i in [
    "Warm compresses twice daily (45°C for 5–10 min) to liquefy meibum",
    "Lid massage and hygiene (lid scrubs or commercial wipes)",
    "Omega-3 fatty acids 1–3 g/day (fish oil or flaxseed oil)",
    "Oral doxycycline 50–100 mg/day for minimum 6–8 weeks (anti-MMP properties)",
    "Azithromycin topical 1% as alternative to systemic tetracyclines",
    "IPL (Intense Pulsed Light) for rosacea-associated MGD",
    "LipiFlow vectored thermal pulsation for meibomian gland obstruction",
    "Meibomian gland intraductal probing for scarring/obstruction",
]:
    story.append(Paragraph(f"• {i}", bullet_style))
story.append(Spacer(1, 0.2*cm))

story.append(Paragraph("5.3  Post-Refractive Surgery / Post-LASIK DED", h2_style))
story.append(Paragraph(
    "Caused by severing corneal nerves during flap creation, reducing afferent reflex arc. "
    "SMILE has lower incidence than LASIK. Usually resolves within 6–12 months.",
    body_style))
for i in [
    "Frequent preservative-free artificial tears from day of surgery",
    "Cyclosporine 0.05% b.i.d. starting 1 month pre-operatively in at-risk patients",
    "Avoid LASIK in patients with pre-existing dry eye (relative contraindication)",
    "Punctal occlusion if symptoms persist beyond 3–6 months",
    "Autologous serum for recalcitrant neurotrophic epitheliopathy",
]:
    story.append(Paragraph(f"• {i}", bullet_style))
story.append(Spacer(1, 0.2*cm))

story.append(info_box(
    "Drug Classes That Can Cause or Worsen Dry Eye",
    [
        "Anticholinergics, antihistamines (H1 and H2 blockers)",
        "Beta-blockers (systemic and topical ophthalmic)",
        "Diuretics (reduces aqueous secretion)",
        "Antidepressants: TCAs, SSRIs",
        "Antipsychotics, antispasmodics",
        "Oral contraceptives, retinoids, isotretinoin (reduces meibomian gland size/function)",
        "Chemotherapy agents",
        "Topical preserved eye drops (benzalkonium chloride/BAK preservative)",
    ],
    border_color=RED))

story.append(PageBreak())

# ══════════════════════════════════════════════════════════════════════════════
# SECTION 6: FOLLOW-UP AND MONITORING
# ══════════════════════════════════════════════════════════════════════════════
story.append(section_header("6. FOLLOW-UP, MONITORING AND PATIENT EDUCATION", GREY_DARK))
story.append(Spacer(1, 0.35*cm))

story.append(Paragraph("Follow-up Schedule", h2_style))
fu_data = [
    ["Severity",              "Initial Review",                              "Subsequent Follow-up"],
    ["Mild (Grade 1)",        "4–6 weeks",                                  "Every 3–6 months; earlier if worsening"],
    ["Moderate (Grade 2)",    "2–4 weeks after starting Csa/Lifitegrast",   "Every 2–3 months; titrate treatment"],
    ["Severe (Grade 3–4)",    "1–2 weeks initially; after procedures",      "Monthly until stable; 3-monthly long-term"],
    ["Post-Refractive Surgery","1 week, 1 month, 3 months, 6 months",       "As clinically indicated"],
]
fu_table = Table(fu_data, colWidths=[4*cm, 4.5*cm, 6.5*cm])
fu_table.setStyle(TableStyle([
    ('BACKGROUND', (0,0), (-1,0), GREY_DARK),
    ('TEXTCOLOR',  (0,0), (-1,0), white),
    ('FONTNAME',   (0,0), (-1,0), 'Helvetica-Bold'),
    ('FONTSIZE',   (0,0), (-1,0), 9),
    ('BACKGROUND', (0,1), (-1,-1), white),
    ('ROWBACKGROUNDS', (0,1), (-1,-1), [white, ROW_ALT]),
    ('ALIGN',      (0,0), (-1,-1), 'LEFT'),
    ('VALIGN',     (0,0), (-1,-1), 'MIDDLE'),
    ('FONTNAME',   (0,1), (-1,-1), 'Helvetica'),
    ('FONTSIZE',   (0,1), (-1,-1), 8.5),
    ('GRID',       (0,0), (-1,-1), 0.5, GREY_MID),
    ('ROWPADDING', (0,0), (-1,-1), [6, 4, 6, 4]),
]))
story.append(fu_table)
story.append(Spacer(1, 0.25*cm))

story.append(Paragraph("Treatment Response Assessment", h2_style))
for i in [
    "OSDI or SPEED score reduction (aim for >25% improvement from baseline)",
    "TBUT improvement (target > 10 sec non-invasive)",
    "Reduction in corneal/conjunctival staining grade",
    "Tear osmolarity normalization (< 308 mOsm/L or inter-eye difference < 8 mOsm/L)",
    "MMP-9 negativity on InflammaDry",
    "Schirmer improvement (in aqueous-deficient DED)",
    "Patient-reported improvement in daily activities and quality of life",
]:
    story.append(Paragraph(f"• {i}", bullet_style))
story.append(Spacer(1, 0.2*cm))

story.append(Paragraph("Key Patient Education Points", h2_style))
for i in [
    "DED is a chronic condition requiring long-term management — not a quick fix",
    "Compliance with regular instillation is essential even when asymptomatic",
    "Use preservative-free drops if instilling more than 4 times per day",
    "Cyclosporine and lifitegrast require weeks-to-months for full benefit",
    "Take 20-20-20 breaks during screen use: every 20 min, look 20 ft away for 20 sec",
    "Blink consciously during prolonged screen/reading/TV time",
    "Avoid rubbing eyes — exacerbates inflammation and surface damage",
    "Lubricating ointment at bedtime for 3–6 months after epithelial healing reduces recurrence",
]:
    story.append(Paragraph(f"• {i}", bullet_style))

story.append(PageBreak())

# ══════════════════════════════════════════════════════════════════════════════
# SECTION 7: REFERENCES
# ══════════════════════════════════════════════════════════════════════════════
story.append(section_header("7. REFERENCES", GREY_DARK))
story.append(Spacer(1, 0.35*cm))

refs = [
    ("1", "TFOS DEWS II Executive Summary.",
     "Craig JP, Nelson JD, Azar DT, et al.",
     "Ocul Surf. 2017;15(4):802–812. doi:10.1016/j.jtos.2017.08.003"),
    ("2", "TFOS DEWS II Definition and Classification Report.",
     "Stapleton F, Alves M, Bunya VY, et al.",
     "Ocul Surf. 2017;15(3):276–283."),
    ("3", "TFOS DEWS II Pathophysiology Report.",
     "Bron AJ, de Paiva CS, Chauhan SK, et al.",
     "Ocul Surf. 2017;15(3):438–510."),
    ("4", "TFOS DEWS II Diagnostic Methodology Report.",
     "Wolffsohn JS, Arita R, Chalmers R, et al.",
     "Ocul Surf. 2017;15(3):539–574."),
    ("5", "TFOS DEWS II Management and Therapy Report.",
     "Jones L, Downie LE, Korb D, et al.",
     "Ocul Surf. 2017;15(3):575–628."),
    ("6", "Kanski's Clinical Ophthalmology: A Systematic Approach, 10th Ed.",
     "Bowling B.",
     "Elsevier, 2024. Chapter 5."),
    ("7", "The Wills Eye Manual, 8th Edition.",
     "Wills Eye Hospital.",
     "Wolters Kluwer, 2022. Section 4.3: Dry Eye Syndrome, pp. 171–176."),
    ("8", "Goodman & Gilman's The Pharmacological Basis of Therapeutics, 14th Ed.",
     "Brunton LL, et al. (Eds.).",
     "McGraw-Hill, 2023."),
    ("9", "Association between potential factors and dry eye disease: A systematic review.",
     "Yang K, Wu S, Ke L.",
     "Medicine (Baltimore). 2024 Dec 27. PMID: 39969375."),
]

ref_data = [["No.", "Title", "Authors", "Source"]]
for no, title, authors, source in refs:
    ref_data.append([no, title, authors, source])

ref_table = Table(ref_data, colWidths=[0.6*cm, 5.5*cm, 4.5*cm, 4.4*cm])
ref_table.setStyle(TableStyle([
    ('BACKGROUND', (0,0), (-1,0), GREY_DARK),
    ('TEXTCOLOR',  (0,0), (-1,0), white),
    ('FONTNAME',   (0,0), (-1,0), 'Helvetica-Bold'),
    ('FONTSIZE',   (0,0), (-1,0), 8.5),
    ('BACKGROUND', (0,1), (-1,-1), white),
    ('ROWBACKGROUNDS', (0,1), (-1,-1), [white, ROW_ALT]),
    ('ALIGN',      (0,0), (-1,-1), 'LEFT'),
    ('VALIGN',     (0,0), (-1,-1), 'TOP'),
    ('FONTNAME',   (0,1), (-1,-1), 'Helvetica'),
    ('FONTSIZE',   (0,1), (-1,-1), 7.5),
    ('GRID',       (0,0), (-1,-1), 0.3, GREY_MID),
    ('ROWPADDING', (0,0), (-1,-1), [4, 3, 4, 3]),
]))
story.append(ref_table)

story.append(Spacer(1, 0.5*cm))
story.append(HRFlowable(width="100%", thickness=0.75, color=GREY_MID))
story.append(Spacer(1, 0.2*cm))
story.append(Paragraph(
    "This document is a clinical reference for educational purposes. "
    "All management decisions should be individualized based on clinical assessment. "
    "For the full TFOS DEWS II reports visit: www.tearfilm.org",
    ref_style))

# ─── BUILD ────────────────────────────────────────────────────────────────────
doc.build(story)
print(f"PDF generated: {OUTPUT}")
Running Command

cd /tmp/workspace/dry-eye-pdf && python generate_dry_eye_white.py 2>&1

Running Command

python3 -c "from pypdf import PdfReader; r = PdfReader('/tmp/workspace/dry-eye-pdf/Dry_Eye_Disease_Clinical_Reference.pdf'); print(f'Pages: {len(r.pages)}')" && ls -lh /tmp/workspace/dry-eye-pdf/Dry_Eye_Disease_Clinical_Reference.pdf

Done. Here's the updated PDF with a full white background:

Dry Eye Disease Clinical Reference

PDF Document · PDF

What changed:
  • All page backgrounds are now pure white
  • Cover page: white background with a dark blue top banner and clean white lower half — no colored fills
  • Section banners: same colored headers (dark blue/teal/green/red/purple) but only as thin strips for headings, not full-page fills
  • Info boxes: white background with a colored left border accent — no tinted fills
  • Two-column classification boxes: white with colored left border only
  • Level boxes (management steps): white background, colored left border and outline only
  • Tables: white + very subtle #FAFAFA alternating rows — no colored row fills
  • All body text, bullets, and data remain exactly the same content
This is a shared conversation. Sign in to Orris to start your own chat.