Discuss the role of cytology in the diagnosis of lower respiratory tract lesions 10 marker for rguhs md pathology university exam
sputum cytology lung cancer squamous cell carcinoma cytologic smear

Fine-needle aspiration cytology (FNAC) smear of a left cervical lymph node demonstrates malignant squamous cells consistent with metastatic disease from a hypopharyngeal primary. The specimen is a pap-stained cytology smear prepared from a neck mass in a 55-year-old male with a year-long history of an enlarging neck mass and a known biopsy-confirmed squamous cell carcinoma of the pyriform sinus. Light microscopy reveals cohesive cellular clusters and individual polygonal cells with abundant eosinophilic cytoplasm, hyperchromatic enlarged nuclei, conspicuous nucleoli, and prominent intercellular bridges. Occasional keratinization points and necrotic background are seen, with a mixed lymphoid population and scant mucinous material. The cytologic features are classic for squamous cell carcinoma, including high nuclear-to-cytoplasmic ratio and marked cellular pleomorphism. The presence of metastatic squamous carcinoma in a cervical lymph node confirms regional nodal involvement and supports the diagnosis of disseminated disease from an oropharyngeal-hypopharyngeal primary, especially given the pyriform sinus origin. Clinically, this finding influences treatment planning, including neck dissection versus chemoradiation and prognosis assessment. Correlation with imaging (CT/MRI) and endoscopic evaluation is recommended to identify the primary site, assess nodal burden, and guide staged management. Overall, the image exemplifies cytologic hallmarks of metastatic squamous carcinoma in lymphoid tissue, with diagnostic significance for head-and-neck oncology, nodal metastasis, and therapeutic decision-making.

This composite figure illustrates the diagnostic findings for lung squamous cell carcinoma through radiological and histopathological imaging. Panels (A) and (B) show axial computed tomography (CT) scans of the thorax. Panel (A) is a non-contrast CT, and (B) is a contrast-enhanced CT, both revealing a spiculated mass measuring approximately 35 x 20 mm in the lower right lung lobe. The mass exhibits heterogeneous density and irregular, radiating margins characteristic of malignancy. Panels (C) and (D) present histopathological images of an endobronchial biopsy specimen stained with hematoxylin and eosin (H&E). The microscopy reveals nests of atypical squamous cells infiltrating the lung parenchyma. The cells show enlarged, hyperchromatic nuclei, prominent nucleoli, and a high nuclear-to-cytoplasmic ratio. While the cellular arrangement into well-defined nests is diagnostic of squamous cell carcinoma, there is a notable absence of overt keratin pearls or intercellular bridges in these specific fields. This educational material integrates macroscopic imaging findings with microscopic cellular architecture to demonstrate the presentation of primary lung cancer.

Digital whole-slide histopathology image of a peripherally located lung squamous cell carcinoma captured on a hematoxylin and eosin stained section. The specimen represents peripheral parenchyma with a bronchogenic carcinoma infiltrating the lung periphery. On low- and high-power inspection, nests and irregular clusters of polygonal tumor cells extend from the capsule into adjacent stroma. Tumor cells exhibit eosinophilic cytoplasm, distinct cell borders, and prominent nuclei with coarse chromatin; intercellular bridges may be visible, and occasional keratinization features can be present in well-differentiated foci. The surrounding lung parenchyma shows desmoplastic stromal response and inflammatory infiltrate. The lesion demonstrates peripheral localization, an infiltrative growth pattern, and disruption of normal alveolar architecture. The image supports a histopathologic diagnosis of squamous cell carcinoma due to squamous differentiation, keratinization in places, and intercellular keratin bridges; such morphology is typical in smokers with bronchogenic carcinoma. Clinically, this histology correlates with a smoking history and indicates a solid, non-small cell lung cancer variant with squamous lineage. This image would be valuable for educational purposes, pathology review, differential diagnosis with adenocarcinoma or large cell carcinoma, and in teaching about tumor-stroma interactions in peripheral lung cancers. Correlation with clinical history enhances interpretive accuracy and guides multidisciplinary care in treatment planning.
| Step | Method |
|---|---|
| Centrifugation | Concentrate cells from fluid samples |
| Smear preparation | Direct smear (air-dried or alcohol-fixed) |
| Staining | Pap stain, MGG (May-Grünwald Giemsa), H&E |
| Liquid-based cytology (LBC) | Commercial systems (ThinPrep/SurePath) - reduces obscuring elements |
| Cell block (FFPE) | Histologic sections, enables IHC and molecular tests |
| Condition | Cytologic Finding |
|---|---|
| Pulmonary TB | Epithelioid granulomas, Langhans giant cells, caseous necrosis; ZN stain for AFB |
| Pneumocystis jirovecii pneumonia | Foamy "cotton candy" exudate in BAL; GMS stain shows "helmet-shaped" cysts |
| CMV pneumonitis | Intranuclear "owl-eye" inclusions (better on transbronchial biopsy) |
| Aspergillosis | Branching septate hyphae at 45° angles |
| Diffuse alveolar hemorrhage | Hemosiderin-laden macrophages (Prussian blue+) |
| Pulmonary alveolar proteinosis | PAS-positive granular material, foamy macrophages in BAL |
| Lipid pneumonia | Lipid vacuoles in alveolar macrophages (Oil Red O stain) |
| Technique | Sensitivity for Malignancy | Best for |
|---|---|---|
| Sputum cytology | 60-70% | Central SCC |
| Bronchial brushing | 70-80% | Central/endobronchial lesions |
| BAL | 50-70% | Diffuse/peripheral, infections |
| CT-guided FNAC | 85-95% | Peripheral nodules |
| EBUS-TBNA | 85-90% | Mediastinal nodes, staging |

Make one pdf
mkdir -p /tmp/workspace/lrt-cytology && ls /tmp/workspace/
~/lrt-cytology/generate_pdf.py
from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.units import cm, mm
from reportlab.platypus import (
SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
HRFlowable, KeepTogether
)
from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_JUSTIFY
from reportlab.platypus import PageBreak
OUTPUT = "/tmp/workspace/lrt-cytology/Cytology_LRT_Lesions_RGUHS.pdf"
doc = SimpleDocTemplate(
OUTPUT,
pagesize=A4,
rightMargin=2*cm,
leftMargin=2*cm,
topMargin=2.2*cm,
bottomMargin=2*cm,
)
styles = getSampleStyleSheet()
# Custom styles
title_style = ParagraphStyle(
"Title",
parent=styles["Title"],
fontSize=15,
textColor=colors.HexColor("#1a3660"),
spaceAfter=4,
alignment=TA_CENTER,
fontName="Helvetica-Bold",
)
subtitle_style = ParagraphStyle(
"Subtitle",
parent=styles["Normal"],
fontSize=10,
textColor=colors.HexColor("#4a4a4a"),
spaceAfter=2,
alignment=TA_CENTER,
fontName="Helvetica",
)
exam_tag_style = ParagraphStyle(
"ExamTag",
parent=styles["Normal"],
fontSize=9,
textColor=colors.white,
alignment=TA_CENTER,
fontName="Helvetica-Bold",
backColor=colors.HexColor("#1a3660"),
spaceBefore=4,
spaceAfter=8,
borderPad=4,
)
h1_style = ParagraphStyle(
"H1",
parent=styles["Heading1"],
fontSize=12,
textColor=colors.white,
fontName="Helvetica-Bold",
backColor=colors.HexColor("#1a3660"),
spaceBefore=12,
spaceAfter=4,
leftIndent=-0.3*cm,
rightIndent=-0.3*cm,
borderPad=5,
)
h2_style = ParagraphStyle(
"H2",
parent=styles["Heading2"],
fontSize=11,
textColor=colors.HexColor("#1a3660"),
fontName="Helvetica-Bold",
spaceBefore=8,
spaceAfter=3,
borderPad=2,
)
h3_style = ParagraphStyle(
"H3",
parent=styles["Heading3"],
fontSize=10,
textColor=colors.HexColor("#2a5099"),
fontName="Helvetica-Bold",
spaceBefore=6,
spaceAfter=2,
)
body_style = ParagraphStyle(
"Body",
parent=styles["Normal"],
fontSize=9.5,
leading=14,
alignment=TA_JUSTIFY,
spaceAfter=4,
fontName="Helvetica",
)
bullet_style = ParagraphStyle(
"Bullet",
parent=styles["Normal"],
fontSize=9.5,
leading=14,
leftIndent=14,
firstLineIndent=-10,
spaceAfter=2,
fontName="Helvetica",
)
sub_bullet_style = ParagraphStyle(
"SubBullet",
parent=styles["Normal"],
fontSize=9,
leading=13,
leftIndent=28,
firstLineIndent=-10,
spaceAfter=1,
fontName="Helvetica",
)
note_style = ParagraphStyle(
"Note",
parent=styles["Normal"],
fontSize=8.5,
leading=12,
textColor=colors.HexColor("#555555"),
fontName="Helvetica-Oblique",
alignment=TA_CENTER,
spaceAfter=4,
)
tip_style = ParagraphStyle(
"Tip",
parent=styles["Normal"],
fontSize=9,
leading=13,
textColor=colors.HexColor("#7a3800"),
fontName="Helvetica-Bold",
backColor=colors.HexColor("#fff3e0"),
borderPad=5,
spaceBefore=6,
spaceAfter=6,
leftIndent=4,
)
def bullet(text, sub=False):
st = sub_bullet_style if sub else bullet_style
return Paragraph(f"\u2022 {text}", st)
def h1(text):
return Paragraph(f" {text}", h1_style)
def h2(text):
return Paragraph(text, h2_style)
def h3(text):
return Paragraph(text, h3_style)
def body(text):
return Paragraph(text, body_style)
def spacer(h=6):
return Spacer(1, h)
def hr():
return HRFlowable(width="100%", thickness=0.5, color=colors.HexColor("#cccccc"), spaceAfter=4, spaceBefore=4)
# ---------- TABLE HELPER ----------
def make_table(headers, rows, col_widths=None):
data = [headers] + rows
tbl = Table(data, colWidths=col_widths, repeatRows=1)
style = TableStyle([
("BACKGROUND", (0, 0), (-1, 0), colors.HexColor("#1a3660")),
("TEXTCOLOR", (0, 0), (-1, 0), colors.white),
("FONTNAME", (0, 0), (-1, 0), "Helvetica-Bold"),
("FONTSIZE", (0, 0), (-1, 0), 9),
("ALIGN", (0, 0), (-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), [colors.HexColor("#f0f4fa"), colors.white]),
("GRID", (0, 0), (-1, -1), 0.4, colors.HexColor("#aaaaaa")),
("TOPPADDING", (0, 0), (-1, -1), 4),
("BOTTOMPADDING", (0, 0), (-1, -1), 4),
("LEFTPADDING", (0, 0), (-1, -1), 6),
("RIGHTPADDING", (0, 0), (-1, -1), 6),
])
tbl.setStyle(style)
return tbl
# ===== BUILD CONTENT =====
story = []
# --- TITLE BLOCK ---
story.append(Paragraph("Role of Cytology in the Diagnosis of", title_style))
story.append(Paragraph("Lower Respiratory Tract Lesions", title_style))
story.append(spacer(2))
story.append(Paragraph("RGUHS MD Pathology University Examination | 10 Marks", subtitle_style))
story.append(spacer(4))
story.append(HRFlowable(width="100%", thickness=2, color=colors.HexColor("#1a3660"), spaceAfter=6))
story.append(Paragraph("Sources: Murray & Nadel's Textbook of Respiratory Medicine | Fishman's Pulmonary Diseases and Disorders", note_style))
story.append(spacer(8))
# --- INTRODUCTION ---
story.append(h1("1. Introduction"))
story.append(spacer(4))
story.append(body(
"Cytology of the lower respiratory tract (LRT) involves the microscopic examination of cells obtained "
"from the tracheobronchial tree and lung parenchyma. It is a minimally invasive, cost-effective, and rapid "
"diagnostic tool for both <b>neoplastic and non-neoplastic</b> conditions. As defined in Murray & Nadel's "
"Textbook of Respiratory Medicine, cytology is <i>'a subfield of pathology that uses cells obtained from fluids "
"or removed from tissues to determine a diagnosis.'</i> Common thoracic cytology specimens include sputum, "
"pleural fluid aspirates, bronchoalveolar lavage (BAL) fluid, and fine needle aspiration (FNA) samples."
))
# --- SPECIMENS ---
story.append(h1("2. Specimens Used in LRT Cytology"))
story.append(spacer(4))
story.append(h2("2.1 Sputum Cytology"))
story.append(bullet("Collection: Spontaneously expectorated or induced (hypertonic saline nebulization)"))
story.append(bullet("3 consecutive early-morning deep-cough specimens preferred for optimal sensitivity"))
story.append(bullet("Staining: Papanicolaou (Pap) stain or H&E"))
story.append(bullet("Sensitivity: ~65–70% for central tumors (SCC); lower (~20–30%) for peripheral tumors"))
story.append(bullet("Best for: Exfoliating central squamous cell carcinoma; screening in high-risk smokers"))
story.append(bullet("Limitations: High false-negative rate for peripheral adenocarcinomas; false positives with reactive atypia"))
story.append(spacer(4))
story.append(h2("2.2 Bronchial Washings and Brushings (via Fiberoptic Bronchoscopy)"))
story.append(bullet("Washings: Saline instilled into the affected segment and aspirated"))
story.append(bullet("Brushings: Cytology brush scrapes the lesion surface — higher cellular yield"))
story.append(bullet("Sensitivity for central lesions: 70–80%; combined with biopsy: ~90%"))
story.append(bullet("Useful for exophytic endobronchial lesions and submucosal infiltrating tumors"))
story.append(spacer(4))
story.append(h2("2.3 Bronchoalveolar Lavage (BAL)"))
story.append(bullet("Normal saline instilled into the distal airspace via a wedged bronchoscope, then aspirated"))
story.append(bullet("Safe even in critically ill patients when biopsy is contraindicated"))
story.append(bullet("Analysis: Cell count + differential, cytology, microbiology, flow cytometry, molecular tests"))
story.append(bullet("Key diagnostic uses:"))
story.append(bullet("Pneumocystis jirovecii pneumonia (most common diagnostic method — foamy exudate)", sub=True))
story.append(bullet("Diffuse alveolar hemorrhage (hemosiderin-laden macrophages, Prussian blue+)", sub=True))
story.append(bullet("Pulmonary alveolar proteinosis (milky fluid, PAS-positive granular material)", sub=True))
story.append(bullet("Malignancy — adenocarcinoma spreading along alveolar walls", sub=True))
story.append(bullet("Opportunistic infections in immunocompromised patients (CMV, Aspergillus)", sub=True))
story.append(spacer(4))
story.append(h2("2.4 Fine Needle Aspiration Cytology (FNAC)"))
story.append(bullet("CT-guided / Ultrasound-guided FNAC: Peripheral pulmonary nodules, pleural & chest wall lesions"))
story.append(bullet("TBNA (Transbronchial Needle Aspiration): Paratracheal/parabronchial lesions, mediastinal nodes"))
story.append(bullet("EBUS-TBNA: Real-time ultrasound-guided FNA; gold standard for mediastinal staging"))
story.append(bullet("Sensitivity for peripheral malignancies: 85–95% (CT-guided)"))
story.append(bullet("Rapid On-Site Evaluation (ROSE): Cytopathologist assesses adequacy in real-time, reduces failed passes"))
story.append(bullet("Cell block (FFPE) enables IHC and molecular testing comparable to core biopsy"))
story.append(spacer(4))
story.append(h2("2.5 Pleural Fluid Cytology"))
story.append(bullet("Centrifuged exudate may reveal malignant cells in lung carcinoma with pleural involvement"))
story.append(bullet("Cell block preparation allows immunohistochemistry for tumor typing"))
# --- PROCESSING ---
story.append(h1("3. Specimen Processing"))
story.append(spacer(4))
story.append(body("Fluid samples are centrifuged to concentrate cells; aspirate smears are prepared directly. "
"Staining with Pap (alcohol-fixed) or MGG/H&E (air-dried) is standard. "
"Liquid-based cytology (LBC) systems (ThinPrep/SurePath) reduce obscuring elements and improve cell preservation. "
"FFPE cell blocks allow conventional sectioning, IHC, and archival storage, though tissue architecture is not preserved."))
story.append(spacer(4))
proc_headers = [Paragraph(h, ParagraphStyle("th", fontName="Helvetica-Bold", fontSize=9, textColor=colors.white)) for h in ["Step", "Method / Purpose"]]
proc_rows = [
["Centrifugation", "Concentrates cells from fluid samples (pleural fluid, BAL)"],
["Smear preparation", "Direct smear — air-dried or alcohol-fixed"],
["Staining", "Pap stain, MGG (May-Grünwald Giemsa), H&E"],
["Liquid-based cytology", "ThinPrep/SurePath — reduces obscuring elements, standardises prep"],
["Cell block (FFPE)", "Histologic sections — enables IHC, molecular tests, long-term storage"],
["ROSE", "Rapid on-site evaluation for adequacy at time of FNA procedure"],
]
story.append(make_table(proc_headers, proc_rows, col_widths=[4.5*cm, 11.5*cm]))
# --- CYTOLOGIC FEATURES ---
story.append(h1("4. Cytologic Features of Specific LRT Lesions"))
story.append(spacer(4))
# Malignant
story.append(h2("A. Squamous Cell Carcinoma (SCC)"))
story.append(bullet("Most amenable to sputum cytology — central, exfoliating tumour"))
story.append(bullet("Cytology: Pleomorphic cells with dense orangeophilic / 'glassy' cytoplasm"))
story.append(bullet("Hyperchromatic, irregular nuclei; tadpole cells, fibre cells, keratin pearls"))
story.append(bullet("Intercellular bridges visible in cell clusters"))
story.append(bullet("Background: Necrotic ('dirty') diathesis"))
story.append(bullet("IHC: p40+, CK5/6+, TTF-1 negative"))
story.append(spacer(4))
story.append(h2("B. Adenocarcinoma"))
story.append(bullet("Peripheral tumour — better sampled by CT-guided FNAC or BAL"))
story.append(bullet("Cytology: Cells in acinar, papillary, or 3D ball-like (morula) clusters"))
story.append(bullet("Columnar/cuboidal cells; pale or vacuolated cytoplasm; prominent nucleoli"))
story.append(bullet("Mucin in cytoplasm (mucicarmine positive)"))
story.append(bullet("IHC: TTF-1+, Napsin A+ (confirms lung primary)"))
story.append(spacer(4))
story.append(h2("C. Small Cell Lung Carcinoma (SCLC)"))
story.append(bullet("Centrally located; highly aggressive; do not benefit from surgery"))
story.append(bullet("Cytology: Classic 'oat cells' — small, scant cytoplasm"))
story.append(bullet("Nuclear moulding; 'salt-and-pepper' (finely granular) chromatin; crush artefact"))
story.append(bullet("IHC: CD56+, synaptophysin+, chromogranin+, TTF-1+ (~90%); Ki-67 >60%"))
story.append(spacer(4))
story.append(h2("D. Large Cell Neuroendocrine Carcinoma (LCNEC)"))
story.append(bullet("Large cells with ample cytoplasm; prominent nucleoli"))
story.append(bullet("Rosette-like architecture; areas of extensive necrosis; >10 mitoses/2 mm²"))
story.append(bullet("IHC: Neuroendocrine markers positive; Ki-67 >40%"))
story.append(spacer(4))
story.append(h2("E. Carcinoid Tumour (Typical and Atypical)"))
story.append(bullet("Cytology: Uniform, round cells with granular 'salt-and-pepper' chromatin; moderate cytoplasm"))
story.append(bullet("Typical carcinoid: <2 mitoses/mm², no necrosis; Ki-67 <5%"))
story.append(bullet("Atypical carcinoid: 2–10 mitoses/mm², focal necrosis; Ki-67 5–20%"))
story.append(bullet("IHC: Synaptophysin+, chromogranin+, CD56+"))
story.append(spacer(6))
# Non-neoplastic table
story.append(h2("F. Non-neoplastic / Infective Conditions"))
story.append(spacer(4))
nn_headers = [Paragraph(h, ParagraphStyle("th", fontName="Helvetica-Bold", fontSize=9, textColor=colors.white))
for h in ["Condition", "Cytologic Finding", "Special Stain"]]
nn_rows = [
["Pulmonary Tuberculosis", "Epithelioid granulomas, Langhans giant cells, caseous necrosis", "ZN stain for AFB"],
["Pneumocystis jirovecii", "Foamy 'cotton candy' exudate in BAL; helmet-shaped cysts", "GMS / Giemsa"],
["CMV Pneumonitis", "Intranuclear 'owl-eye' inclusions (better on TBB)", "IHC / H&E"],
["Aspergillosis", "Branching septate hyphae at 45° angles", "GMS / PAS"],
["Diffuse Alveolar Haemorrhage", "Haemosiderin-laden macrophages", "Prussian blue"],
["Pulmonary Alveolar Proteinosis", "PAS-positive granular material; foamy macrophages in BAL", "PAS"],
["Lipid Pneumonia", "Lipid vacuoles in alveolar macrophages", "Oil Red O"],
]
story.append(make_table(nn_headers, nn_rows, col_widths=[4.5*cm, 8*cm, 3.5*cm]))
# --- ANCILLARY ---
story.append(h1("5. Role of Ancillary Tests on Cytology Material"))
story.append(spacer(4))
story.append(body("Modern cytology integrates morphology with ancillary techniques, maximising diagnostic information from small samples:"))
ancillary_data = [
["1. Immunohistochemistry (IHC)",
"On cell blocks — TTF-1, p40, CK5/6, CD56, synaptophysin, chromogranin\n"
"Distinguishes adenocarcinoma vs SCC vs SCLC and confirms lung primary"],
["2. Molecular Testing",
"EGFR mutation, ALK/ROS1 rearrangement, KRAS mutation, PD-L1 expression\n"
"Guides targeted therapy and immunotherapy; performable on FNAC cell blocks"],
["3. Flow Cytometry",
"Lymphoma classification on BAL or pleural fluid"],
["4. Microbiology",
"Simultaneous culture from BAL or bronchial wash for infection diagnosis"],
["5. PCR / Molecular",
"Pathogen detection (TB, viral), driver mutation analysis, DNA methylation"],
]
anc_tbl = Table(ancillary_data, colWidths=[5*cm, 11*cm])
anc_tbl.setStyle(TableStyle([
("BACKGROUND", (0, 0), (0, -1), colors.HexColor("#e8eef7")),
("FONTNAME", (0, 0), (0, -1), "Helvetica-Bold"),
("FONTSIZE", (0, 0), (-1, -1), 9),
("FONTNAME", (1, 0), (1, -1), "Helvetica"),
("VALIGN", (0, 0), (-1, -1), "TOP"),
("GRID", (0, 0), (-1, -1), 0.4, colors.HexColor("#aaaaaa")),
("ROWBACKGROUNDS", (1, 0), (1, -1), [colors.white, colors.HexColor("#f8faff")]),
("TOPPADDING", (0, 0), (-1, -1), 5),
("BOTTOMPADDING", (0, 0), (-1, -1), 5),
("LEFTPADDING", (0, 0), (-1, -1), 6),
]))
story.append(anc_tbl)
# --- SENSITIVITY TABLE ---
story.append(h1("6. Sensitivity and Limitations"))
story.append(spacer(4))
sens_headers = [Paragraph(h, ParagraphStyle("th", fontName="Helvetica-Bold", fontSize=9, textColor=colors.white))
for h in ["Technique", "Sensitivity for Malignancy", "Best For"]]
sens_rows = [
["Sputum cytology", "60–70%", "Central SCC in smokers"],
["Bronchial brushing", "70–80%", "Central / endobronchial lesions"],
["Bronchoalveolar lavage", "50–70%", "Diffuse / peripheral; infections"],
["CT-guided FNAC", "85–95%", "Peripheral pulmonary nodules"],
["EBUS-TBNA", "85–90%", "Mediastinal nodes; staging"],
["Pleural fluid cytology", "40–60%", "Malignant pleural effusions"],
]
story.append(make_table(sens_headers, sens_rows, col_widths=[4.5*cm, 5.5*cm, 6*cm]))
story.append(spacer(6))
story.append(h2("Causes of False Positives:"))
story.append(bullet("Reactive atypia — pulmonary infarction, radiation changes, repair"))
story.append(bullet("Viral cytopathic effect mimicking malignancy"))
story.append(h2("Causes of False Negatives:"))
story.append(bullet("Peripheral lesions not exfoliating into airways"))
story.append(bullet("Sampling error; well-differentiated tumours with minimal atypia"))
story.append(bullet("Necrotic or fibrotic tumours with poor cellularity"))
# --- ADVANTAGES ---
story.append(h1("7. Advantages of Cytology Over Histologic Biopsy"))
story.append(spacer(4))
adv_rows = [
["Minimally invasive", "Fewer complications vs. surgical / core biopsy"],
["Rapid turnaround", "ROSE gives same-day guidance; faster treatment initiation"],
["Multiple modalities", "Several techniques available for the same lesion"],
["Ancillary-compatible", "Cell blocks enable IHC and molecular testing comparable to histology"],
["Safe in critically ill", "BAL can be performed when biopsy is contraindicated"],
["Cost-effective", "Cheaper, widely available, outpatient-friendly"],
]
adv_tbl = Table(adv_rows, colWidths=[5*cm, 11*cm])
adv_tbl.setStyle(TableStyle([
("BACKGROUND", (0, 0), (0, -1), colors.HexColor("#e8eef7")),
("FONTNAME", (0, 0), (0, -1), "Helvetica-Bold"),
("FONTSIZE", (0, 0), (-1, -1), 9),
("FONTNAME", (1, 0), (1, -1), "Helvetica"),
("VALIGN", (0, 0), (-1, -1), "MIDDLE"),
("GRID", (0, 0), (-1, -1), 0.4, colors.HexColor("#aaaaaa")),
("ROWBACKGROUNDS", (1, 0), (1, -1), [colors.white, colors.HexColor("#f8faff")]),
("TOPPADDING", (0, 0), (-1, -1), 5),
("BOTTOMPADDING", (0, 0), (-1, -1), 5),
("LEFTPADDING", (0, 0), (-1, -1), 6),
]))
story.append(adv_tbl)
# --- CONCLUSION ---
story.append(h1("8. Conclusion"))
story.append(spacer(4))
story.append(body(
"Cytology plays an indispensable role in the diagnosis of lower respiratory tract lesions. "
"From simple sputum smears to EBUS-guided FNA, cytologic techniques offer a wide spectrum of tools "
"for diagnosing malignant and non-malignant pulmonary conditions. The integration of liquid-based cytology, "
"rapid on-site evaluation, immunohistochemistry, and molecular testing on cytologic material has made it "
"a cornerstone of modern pulmonary pathology and oncology. Specimen selection should be guided by lesion "
"location, clinical context, and the differential diagnosis."
))
story.append(spacer(8))
story.append(HRFlowable(width="100%", thickness=1.5, color=colors.HexColor("#1a3660"), spaceAfter=6))
# --- EXAM TIP ---
story.append(Paragraph(
"EXAM TIP (10 Marks): Introduction (1) + Specimens & techniques (3) + "
"Cytologic features of neoplastic & non-neoplastic lesions (4) + "
"Ancillary tests & limitations (2). Always draw the sensitivity comparison table.",
tip_style
))
story.append(spacer(6))
story.append(Paragraph(
"Sources: Murray & Nadel's Textbook of Respiratory Medicine, 2-Vol | "
"Fishman's Pulmonary Diseases and Disorders, 2-Vol Set | "
"Robbins & Cotran Pathologic Basis of Disease",
note_style
))
# BUILD
doc.build(story)
print(f"PDF saved to: {OUTPUT}")
cd /tmp/workspace/lrt-cytology && python generate_pdf.py
Cytology LRT Lesions RGUHS
PDF Document · PDF
Pdf on Discuss the role of FNA in the evaluation of mediastinal masses using flowchart And line diagram for university exam
~/lrt-cytology/generate_mediastinal_fna.py
from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.units import cm, mm
from reportlab.platypus import (
SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
HRFlowable, KeepTogether, PageBreak
)
from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_JUSTIFY
from reportlab.graphics.shapes import (
Drawing, Rect, String, Line, Polygon, Circle,
Arrow, Group, Path
)
from reportlab.graphics import renderPDF
from reportlab.platypus import Flowable
import math
OUTPUT = "/tmp/workspace/lrt-cytology/FNA_Mediastinal_Masses_RGUHS.pdf"
W, H = A4
doc = SimpleDocTemplate(OUTPUT, pagesize=A4,
rightMargin=1.8*cm, leftMargin=1.8*cm,
topMargin=2*cm, bottomMargin=2*cm)
styles = getSampleStyleSheet()
BLU = colors.HexColor("#1a3660")
LBLU = colors.HexColor("#e8eef7")
ORG = colors.HexColor("#e65100")
GRN = colors.HexColor("#1b5e20")
YEL = colors.HexColor("#fff8e1")
GRY = colors.HexColor("#f5f5f5")
RED = colors.HexColor("#b71c1c")
def sty(name, **kw):
base = styles.get(name, styles["Normal"])
return ParagraphStyle(name+"_c", parent=base, **kw)
title_s = sty("Title", fontSize=16, textColor=BLU, alignment=TA_CENTER, fontName="Helvetica-Bold", spaceAfter=2)
sub_s = sty("Normal", fontSize=9, textColor=colors.HexColor("#555"), alignment=TA_CENTER, spaceAfter=6)
h1_s = sty("Normal", fontSize=12, textColor=colors.white, fontName="Helvetica-Bold", backColor=BLU,
spaceBefore=10, spaceAfter=4, leftIndent=-2, rightIndent=-2, borderPad=5)
h2_s = sty("Normal", fontSize=11, textColor=BLU, fontName="Helvetica-Bold", spaceBefore=8, spaceAfter=3)
h3_s = sty("Normal", fontSize=10, textColor=colors.HexColor("#2a5099"), fontName="Helvetica-Bold", spaceBefore=5, spaceAfter=2)
body_s = sty("Normal", fontSize=9.5, leading=14, alignment=TA_JUSTIFY, spaceAfter=4)
bul_s = sty("Normal", fontSize=9.5, leading=14, leftIndent=14, firstLineIndent=-10, spaceAfter=2)
sbul_s = sty("Normal", fontSize=9, leading=13, leftIndent=28, firstLineIndent=-10, spaceAfter=1)
note_s = sty("Normal", fontSize=8, textColor=colors.HexColor("#666"), alignment=TA_CENTER,
fontName="Helvetica-Oblique", spaceAfter=4)
tip_s = sty("Normal", fontSize=9, textColor=colors.HexColor("#7a3800"), fontName="Helvetica-Bold",
backColor=YEL, borderPad=5, spaceBefore=6, spaceAfter=6, leftIndent=4)
def b(t): return Paragraph(f"\u2022 {t}", bul_s)
def sb(t): return Paragraph(f"\u2013 {t}", sbul_s)
def sp(h=6):return Spacer(1, h)
def hr(): return HRFlowable(width="100%", thickness=0.5, color=colors.HexColor("#ccc"), spaceAfter=3, spaceBefore=3)
def h1(t): return Paragraph(f" {t}", h1_s)
def h2(t): return Paragraph(t, h2_s)
def h3(t): return Paragraph(t, h3_s)
def body(t):return Paragraph(t, body_s)
def tbl(headers, rows, widths=None):
data = [headers]+rows
t = Table(data, colWidths=widths, repeatRows=1)
t.setStyle(TableStyle([
("BACKGROUND",(0,0),(-1,0), BLU),
("TEXTCOLOR",(0,0),(-1,0), colors.white),
("FONTNAME",(0,0),(-1,0),"Helvetica-Bold"),
("FONTSIZE",(0,0),(-1,-1), 8.5),
("FONTNAME",(0,1),(-1,-1),"Helvetica"),
("ALIGN",(0,0),(-1,-1),"LEFT"),
("VALIGN",(0,0),(-1,-1),"TOP"),
("ROWBACKGROUNDS",(0,1),(-1,-1),[LBLU, colors.white]),
("GRID",(0,0),(-1,-1), 0.4, colors.HexColor("#aaa")),
("TOPPADDING",(0,0),(-1,-1),4),
("BOTTOMPADDING",(0,0),(-1,-1),4),
("LEFTPADDING",(0,0),(-1,-1),5),
("RIGHTPADDING",(0,0),(-1,-1),5),
]))
return t
# ─────────────────────────────────────────────
# FLOWCHART (ReportLab Drawing)
# ─────────────────────────────────────────────
def rounded_box(d, x, y, w, h, text, fill=BLU, text_col=colors.white,
font="Helvetica-Bold", fsize=8.5, r=6):
d.add(Rect(x, y, w, h, rx=r, ry=r, fillColor=fill, strokeColor=BLU, strokeWidth=0.8))
lines = text.split("\n")
line_h = fsize + 2
total_h = len(lines) * line_h
start_y = y + h/2 + total_h/2 - line_h
for line in lines:
d.add(String(x + w/2, start_y, line,
textAnchor="middle", fillColor=text_col,
fontName=font, fontSize=fsize))
start_y -= line_h
def diamond(d, cx, cy, hw, hh, text, fill=colors.HexColor("#e65100"), text_col=colors.white, fsize=8):
pts = [cx, cy+hh, cx+hw, cy, cx, cy-hh, cx-hw, cy]
d.add(Polygon(pts, fillColor=fill, strokeColor=colors.HexColor("#b34700"), strokeWidth=0.8))
lines = text.split("\n")
line_h = fsize + 1.5
total = len(lines)*line_h
sy = cy + total/2 - line_h/2
for ln in lines:
d.add(String(cx, sy, ln, textAnchor="middle", fillColor=text_col, fontName="Helvetica-Bold", fontSize=fsize))
sy -= line_h
def arrow_down(d, x, y1, y2, col=BLU):
d.add(Line(x, y1, x, y2+5, strokeColor=col, strokeWidth=1.2))
d.add(Polygon([x, y2, x-4, y2+8, x+4, y2+8], fillColor=col, strokeColor=col))
def arrow_right(d, x1, x2, y, col=BLU, label=""):
d.add(Line(x1, y, x2-5, y, strokeColor=col, strokeWidth=1.2))
d.add(Polygon([x2, y, x2-8, y+4, x2-8, y-4], fillColor=col, strokeColor=col))
if label:
d.add(String((x1+x2)/2, y+3, label, textAnchor="middle",
fillColor=col, fontName="Helvetica-Bold", fontSize=7))
def arrow_left(d, x1, x2, y, col=BLU, label=""):
d.add(Line(x1, y, x2+5, y, strokeColor=col, strokeWidth=1.2))
d.add(Polygon([x2, y, x2+8, y+4, x2+8, y-4], fillColor=col, strokeColor=col))
if label:
d.add(String((x1+x2)/2, y+3, label, textAnchor="middle",
fillColor=col, fontName="Helvetica-Bold", fontSize=7))
def make_flowchart():
dw = 500
dh = 720
d = Drawing(dw, dh)
# Background
d.add(Rect(0, 0, dw, dh, fillColor=colors.HexColor("#f9fbff"), strokeColor=colors.HexColor("#ccc"), strokeWidth=0.5))
d.add(String(dw/2, dh-14, "FLOWCHART: FNA in Evaluation of Mediastinal Mass",
textAnchor="middle", fillColor=BLU, fontName="Helvetica-Bold", fontSize=10))
cx = dw//2
bw, bh = 200, 30
# Box 1 – Patient
y1 = dh - 45
rounded_box(d, cx-bw//2, y1-bh, bw, bh, "Patient with Mediastinal Mass\n(CXR / CT Chest)", fill=BLU)
arrow_down(d, cx, y1-bh, y1-bh-18)
# Diamond – IMAGING
y2 = y1 - bh - 18 - 40
diamond(d, cx, y2, 120, 38, "Imaging\nCharacterisation\n(CT/MRI/PET)", fill=ORG)
arrow_down(d, cx, y2-38, y2-38-14)
# Diamond – Compartment
y3 = y2 - 38 - 14 - 38
diamond(d, cx, y3, 120, 36, "Compartment\nLocation?", fill=colors.HexColor("#00695c"))
# Left arrow – Anterior
arrow_left(d, cx-120, cx-120-60, y3, col=BLU, label="Anterior")
# Down – Middle
arrow_down(d, cx, y3-36, y3-36-14)
# Right arrow – Posterior
arrow_right(d, cx+120, cx+120+60, y3, col=BLU, label="Posterior")
# Anterior box
x_ant = cx - 120 - 60 - 75
y_ant = y3 - 10
rounded_box(d, x_ant-70, y_ant-55, 140, 55,
"ANTERIOR\nThymoma\nGerm Cell Tumour\nLymphoma\nRetrosternal Goitre",
fill=colors.HexColor("#1565c0"), fsize=7.5)
# Posterior box
x_post = cx + 120 + 60
y_post = y3 - 10
rounded_box(d, x_post-5, y_post-55, 130, 55,
"POSTERIOR\nNeurogenic Tumour\n(Schwannoma, NF,\nGanglioneuroma,\nNeuroblastoma)",
fill=colors.HexColor("#1565c0"), fsize=7.5)
# Middle
y4 = y3 - 36 - 14 - 30
rounded_box(d, cx-90, y4-26, 180, 26,
"MIDDLE: Lymphoma, Cysts,\nMediastinal Granuloma", fill=colors.HexColor("#1565c0"), fsize=7.5)
arrow_down(d, cx, y4-26, y4-26-12)
# Diamond – FNA approach
y5 = y4 - 26 - 12 - 38
diamond(d, cx, y5, 115, 36, "Choose FNA\nApproach", fill=ORG)
# Three approaches below diamond
arrow_down(d, cx, y5-36, y5-36-12)
y6 = y5 - 36 - 12 - 28
# CT-guided
rounded_box(d, cx-230, y6-26, 130, 28, "CT-guided\nTransthoracic FNAC",
fill=colors.HexColor("#00695c"), fsize=8)
d.add(Line(cx-230+65, y6, cx, y6, strokeColor=BLU, strokeWidth=1))
# EBUS-TBNA
rounded_box(d, cx-65, y6-26, 130, 28, "EBUS-TBNA\n(Para-tracheal/Subcarinal)",
fill=colors.HexColor("#00695c"), fsize=8)
# EUS
rounded_box(d, cx+100, y6-26, 130, 28, "EUS-FNA\n(Posterior/Paraesophageal)",
fill=colors.HexColor("#00695c"), fsize=8)
d.add(Line(cx+100+65, y6, cx, y6, strokeColor=BLU, strokeWidth=1))
arrow_down(d, cx, y6-26, y6-26-12)
# ROSE
y7 = y6 - 26 - 12 - 26
rounded_box(d, cx-100, y7-24, 200, 24,
"Rapid On-Site Evaluation (ROSE)\nAdequacy check by cytopathologist",
fill=colors.HexColor("#6a1b9a"), fsize=8)
arrow_down(d, cx, y7-24, y7-24-12)
# Diamond Adequate?
y8 = y7 - 24 - 12 - 34
diamond(d, cx, y8, 90, 32, "Adequate\nSample?", fill=ORG)
# Yes -> process
arrow_right(d, cx+90, cx+90+50, y8, col=GRN, label="Yes")
rounded_box(d, cx+90+50, y8-22, 110, 28,
"Process: Smear+Cell\nBlock+IHC+Molecular",
fill=GRN, fsize=7.5)
# No -> surgical
arrow_left(d, cx-90, cx-90-50, y8, col=RED, label="No")
rounded_box(d, cx-90-50-100, y8-22, 100, 28,
"Surgical Biopsy\n(Mediastinoscopy)",
fill=RED, fsize=7.5)
# Final diagnosis
arrow_down(d, cx+90+50+55, y8-22, y8-22-30, col=GRN)
y9 = y8 - 22 - 30 - 26
rounded_box(d, cx+90+50, y9-24, 110, 26,
"DIAGNOSIS + Ancillary\nTesting (IHC/Molecular)",
fill=colors.HexColor("#004d40"), fsize=7.5)
return d
# ─────────────────────────────────────────────
# LINE DIAGRAM 1 – Mediastinal Compartments
# ─────────────────────────────────────────────
def make_compartment_diagram():
dw, dh = 480, 300
d = Drawing(dw, dh)
d.add(Rect(0, 0, dw, dh, fillColor=colors.HexColor("#f0f4ff"), strokeColor=colors.HexColor("#bbb"), strokeWidth=0.5))
d.add(String(dw/2, dh-14, "LINE DIAGRAM: Mediastinal Compartments & Common Masses",
textAnchor="middle", fillColor=BLU, fontName="Helvetica-Bold", fontSize=9))
# Chest outline (simplified ellipse path)
cx, cy = dw//2, dh//2 - 10
chest_w, chest_h = 200, 130
# Lung shadows
for side, xo in [("L", -1), ("R", 1)]:
lx = cx + xo*65
d.add(Rect(lx-45 if xo>0 else lx-55, cy-55, 100, 110,
rx=40, ry=40, fillColor=colors.HexColor("#d0e8f8"),
strokeColor=colors.HexColor("#90b8d8"), strokeWidth=1))
# Chest wall outline
d.add(Rect(cx-chest_w//2, cy-chest_h//2, chest_w, chest_h,
rx=20, ry=20, fillColor=colors.Color(0,0,0,0),
strokeColor=colors.HexColor("#444"), strokeWidth=2))
# Compartment dividers
# Anterior/Middle divider (pericardium ~ 40% from front)
x_am = cx - 30
d.add(Line(x_am, cy-chest_h//2+10, x_am, cy+chest_h//2-10,
strokeColor=BLU, strokeWidth=1.2, strokeDashArray=[4,3]))
# Middle/Posterior (vertebral column ~ 60% from front)
x_mp = cx + 40
d.add(Line(x_mp, cy-chest_h//2+10, x_mp, cy+chest_h//2-10,
strokeColor=BLU, strokeWidth=1.2, strokeDashArray=[4,3]))
# Compartment labels inside
d.add(String(cx-chest_w//2+18, cy+5, "ANT",
textAnchor="middle", fillColor=BLU, fontName="Helvetica-Bold", fontSize=9))
d.add(String((x_am+x_mp)//2, cy+5, "MID",
textAnchor="middle", fillColor=BLU, fontName="Helvetica-Bold", fontSize=9))
d.add(String(x_mp+28, cy+5, "POST",
textAnchor="middle", fillColor=BLU, fontName="Helvetica-Bold", fontSize=9))
# Annotations with leader lines
# Anterior - Thymoma
ax, ay = cx - chest_w//2 + 14, cy + 30
d.add(Line(ax, ay, ax - 30, ay + 30, strokeColor=ORG, strokeWidth=0.8))
d.add(String(ax-32, ay+30, "Thymoma", textAnchor="end", fillColor=ORG, fontName="Helvetica", fontSize=7.5))
# Anterior - GCT
d.add(Line(ax, cy - 10, ax - 30, cy - 40, strokeColor=ORG, strokeWidth=0.8))
d.add(String(ax-32, cy-40, "Germ Cell", textAnchor="end", fillColor=ORG, fontName="Helvetica", fontSize=7.5))
# Middle - Lymphoma
mx = (x_am + x_mp)//2
d.add(Line(mx, cy-20, mx, cy-55, strokeColor=GRN, strokeWidth=0.8))
d.add(String(mx, cy-58, "Lymphoma", textAnchor="middle", fillColor=GRN, fontName="Helvetica", fontSize=7.5))
# Middle - Cyst
d.add(Line(mx, cy+20, mx+10, cy+55, strokeColor=GRN, strokeWidth=0.8))
d.add(String(mx+12, cy+58, "Pericardial Cyst", textAnchor="start", fillColor=GRN, fontName="Helvetica", fontSize=7.5))
# Posterior - Neurogenic
px = x_mp + 28
d.add(Line(px, cy, px+38, cy+25, strokeColor=RED, strokeWidth=0.8))
d.add(String(px+40, cy+25, "Neurogenic", textAnchor="start", fillColor=RED, fontName="Helvetica", fontSize=7.5))
d.add(Line(px, cy-20, px+38, cy-40, strokeColor=RED, strokeWidth=0.8))
d.add(String(px+40, cy-40, "Schwannoma", textAnchor="start", fillColor=RED, fontName="Helvetica", fontSize=7.5))
# Spine symbol on right
for i in range(5):
d.add(Rect(cx+chest_w//2-18, cy-chest_h//2+22+i*20, 12, 14,
rx=2, fillColor=colors.HexColor("#ddd"), strokeColor=colors.HexColor("#999"), strokeWidth=0.6))
# Sternum symbol on left
d.add(Rect(cx-chest_w//2+4, cy-chest_h//2+15, 8, 100,
rx=2, fillColor=colors.HexColor("#e8e8e8"), strokeColor=colors.HexColor("#999"), strokeWidth=0.6))
# Label sternum & spine
d.add(String(cx-chest_w//2+8, cy-chest_h//2+8, "S", textAnchor="middle",
fillColor=colors.HexColor("#777"), fontName="Helvetica", fontSize=7))
d.add(String(cx+chest_w//2-12, cy-chest_h//2+8, "V", textAnchor="middle",
fillColor=colors.HexColor("#777"), fontName="Helvetica", fontSize=7))
# Legend
ly = 18
for col, label in [(ORG,"Anterior"), (GRN,"Middle/Visceral"), (RED,"Posterior")]:
d.add(Rect(10, ly-8, 12, 10, fillColor=col, strokeColor=col))
d.add(String(25, ly-3, label, fillColor=col, fontName="Helvetica", fontSize=8))
ly -= 16
return d
# ─────────────────────────────────────────────
# LINE DIAGRAM 2 – FNA Techniques (EBUS vs CT-guided)
# ─────────────────────────────────────────────
def make_fna_technique_diagram():
dw, dh = 480, 240
d = Drawing(dw, dh)
d.add(Rect(0, 0, dw, dh, fillColor=colors.HexColor("#f9fff9"),
strokeColor=colors.HexColor("#bbb"), strokeWidth=0.5))
d.add(String(dw/2, dh-14, "LINE DIAGRAM: FNA Techniques for Mediastinal Masses",
textAnchor="middle", fillColor=BLU, fontName="Helvetica-Bold", fontSize=9))
# ── Left side: CT-guided FNAC ──
lx = 110
cy = dh//2 - 10
# Chest cross-section ellipse
d.add(Rect(lx-70, cy-60, 140, 120, rx=30, ry=30,
fillColor=colors.HexColor("#ddeeff"), strokeColor=colors.HexColor("#88aacc"), strokeWidth=1.5))
# Mass inside
d.add(Circle(lx-15, cy+5, 22, fillColor=colors.HexColor("#ffcccc"),
strokeColor=RED, strokeWidth=1.2))
d.add(String(lx-15, cy+3, "Mass", textAnchor="middle", fillColor=RED, fontName="Helvetica-Bold", fontSize=7))
# Needle
d.add(Line(lx+68, cy+50, lx-15+18, cy+5+12, strokeColor=colors.HexColor("#333"), strokeWidth=2))
d.add(String(lx+58, cy+60, "CT-guided Needle", textAnchor="middle",
fillColor=colors.HexColor("#333"), fontName="Helvetica", fontSize=7.5))
d.add(String(lx, cy-68, "CT-guided Transthoracic FNAC", textAnchor="middle",
fillColor=BLU, fontName="Helvetica-Bold", fontSize=8))
d.add(String(lx, cy-78, "(Anterior/Peripheral masses)", textAnchor="middle",
fillColor=colors.HexColor("#666"), fontName="Helvetica", fontSize=7))
# ── Right side: EBUS-TBNA ──
rx = 360
# Trachea outline
d.add(Rect(rx-20, cy-70, 40, 140, rx=15, ry=15,
fillColor=colors.HexColor("#ffe0b2"), strokeColor=ORG, strokeWidth=1.5))
d.add(String(rx, cy+60, "Trachea / Bronchus", textAnchor="middle",
fillColor=ORG, fontName="Helvetica", fontSize=7))
# Bronchoscope inside
d.add(Rect(rx-5, cy-65, 10, 90, rx=4, fillColor=colors.HexColor("#666"), strokeColor=colors.HexColor("#333"), strokeWidth=0.8))
# Ultrasound probe tip
d.add(Circle(rx, cy+22, 6, fillColor=colors.HexColor("#333"), strokeColor=colors.white, strokeWidth=0.8))
# EBUS ultrasound beam (fan lines)
for ang in [-40, -20, 0, 20, 40]:
rad = math.radians(ang)
ex = rx + math.sin(rad)*50
ey = cy + 22 + math.cos(rad)*(-50) + 50
d.add(Line(rx, cy+22, ex, ey, strokeColor=colors.HexColor("#aaaaff"), strokeWidth=0.6))
# Lymph node / mass lateral to trachea
d.add(Circle(rx+55, cy-5, 20, fillColor=colors.HexColor("#ffcccc"),
strokeColor=RED, strokeWidth=1))
d.add(String(rx+55, cy-7, "LN", textAnchor="middle", fillColor=RED, fontName="Helvetica-Bold", fontSize=7.5))
# TBNA needle
d.add(Line(rx, cy+10, rx+35, cy-5, strokeColor=colors.HexColor("#333"), strokeWidth=1.8))
d.add(String(rx, cy-78, "EBUS-TBNA", textAnchor="middle",
fillColor=BLU, fontName="Helvetica-Bold", fontSize=8))
d.add(String(rx, cy-88, "(Para-tracheal / Subcarinal LN)", textAnchor="middle",
fillColor=colors.HexColor("#666"), fontName="Helvetica", fontSize=7))
# Divider
d.add(Line(dw//2, 20, dw//2, dh-25, strokeColor=colors.HexColor("#ccc"), strokeWidth=0.8, strokeDashArray=[4,3]))
return d
# ─────────────────────────────────────────────
# LINE DIAGRAM 3 – Cytology Processing Flow
# ─────────────────────────────────────────────
def make_processing_diagram():
dw, dh = 480, 130
d = Drawing(dw, dh)
d.add(Rect(0, 0, dw, dh, fillColor=colors.HexColor("#f9f9ff"),
strokeColor=colors.HexColor("#bbb"), strokeWidth=0.5))
d.add(String(dw/2, dh-14, "LINE DIAGRAM: FNA Specimen Processing & Ancillary Testing",
textAnchor="middle", fillColor=BLU, fontName="Helvetica-Bold", fontSize=9))
boxes = [
("FNA\nAspirate", BLU),
("Smear\n(Pap/MGG)", colors.HexColor("#00695c")),
("Cell Block\n(FFPE)", colors.HexColor("#1565c0")),
("IHC\n(CD20,TTF1,\np40,CD56)", ORG),
("Molecular\n(EGFR,ALK,\nMYC,IgH)", RED),
("DIAGNOSIS\n+ Staging", colors.HexColor("#1b5e20")),
]
bw, bh = 62, 52
gap = 14
total = len(boxes)*bw + (len(boxes)-1)*gap
sx = (dw-total)//2
y = (dh-bh)//2 - 5
for i, (label, col) in enumerate(boxes):
bx = sx + i*(bw+gap)
d.add(Rect(bx, y, bw, bh, rx=5, fillColor=col, strokeColor=colors.HexColor("#555"), strokeWidth=0.6))
lines = label.split("\n")
lh = 9
sy2 = y + bh/2 + len(lines)*lh/2 - lh
for ln in lines:
d.add(String(bx+bw/2, sy2, ln, textAnchor="middle",
fillColor=colors.white, fontName="Helvetica-Bold", fontSize=7.5))
sy2 -= lh
if i < len(boxes)-1:
ax = bx + bw + 2
ay = y + bh//2
d.add(Line(ax, ay, ax+gap-4, ay, strokeColor=BLU, strokeWidth=1.2))
d.add(Polygon([ax+gap-2, ay, ax+gap-8, ay+4, ax+gap-8, ay-4],
fillColor=BLU, strokeColor=BLU))
return d
# ─────────────────────────────────────────────
# BUILD STORY
# ─────────────────────────────────────────────
class DrawingFlowable(Flowable):
def __init__(self, drawing, caption=""):
self._drawing = drawing
self._caption = caption
self.width = drawing.width
self.height = drawing.height + (15 if caption else 0)
def draw(self):
renderPDF.draw(self._drawing, self.canv, 0, 15 if self._caption else 0)
if self._caption:
self.canv.setFont("Helvetica-Oblique", 8)
self.canv.setFillColor(colors.HexColor("#555"))
self.canv.drawCentredString(self.width/2, 3, self._caption)
story = []
# TITLE
story.append(Paragraph("Role of Fine Needle Aspiration (FNA) in the", title_s))
story.append(Paragraph("Evaluation of Mediastinal Masses", title_s))
story.append(sp(2))
story.append(Paragraph("RGUHS MD Pathology University Examination | 10 Marks", sub_s))
story.append(HRFlowable(width="100%", thickness=2, color=BLU, spaceAfter=5))
story.append(Paragraph("Sources: Murray & Nadel's Textbook of Respiratory Medicine | Schwartz's Principles of Surgery | Goldman-Cecil Medicine", note_s))
story.append(sp(6))
# 1. INTRODUCTION
story.append(h1("1. Introduction"))
story.append(sp(3))
story.append(body(
"Mediastinal masses represent a heterogeneous group of lesions arising from the three mediastinal compartments. "
"Fine Needle Aspiration Cytology (FNAC) has emerged as the first-line diagnostic tool for tissue characterisation of these masses, "
"being minimally invasive, cost-effective, and rapidly diagnostic. "
"As stated in Schwartz's Principles of Surgery, <i>'the treatment of up to 60% of patients with anterior mediastinal masses "
"is ultimately nonsurgical, so it is essential to understand all options for obtaining adequate tissue for a definitive diagnosis '</i>. "
"FNAC, combined with ancillary testing (IHC, flow cytometry, molecular studies), can provide a definitive diagnosis "
"in the majority of cases, avoiding the need for surgical mediastinoscopy."
))
# 2. MEDIASTINAL COMPARTMENTS
story.append(h1("2. Mediastinal Compartments and Common Masses"))
story.append(sp(3))
story.append(body("The mediastinum is divided into three compartments, each associated with characteristic lesions. "
"Imaging-based compartment localisation guides the FNA approach."))
story.append(sp(4))
story.append(DrawingFlowable(make_compartment_diagram(),
"Figure 1. Line diagram of mediastinal compartments and characteristic masses (S = Sternum, V = Vertebral column)"))
story.append(sp(6))
comp_h = ["Compartment", "Boundaries", "Common Masses", "FNA Approach"]
comp_r = [
["Anterior\n(Prevascular)", "Sternum → anterior\npericardium",
"Thymoma, Germ cell tumour,\nLymphoma, Retrosternal goitre,\nLipoma, Thymic cyst",
"CT-guided\ntransthoracic FNAC"],
["Middle\n(Visceral)", "Anterior pericardium\n→ anterior vertebra",
"Lymphoma, Pericardial cyst,\nMediastinal granuloma,\nEsophageal tumour",
"EBUS-TBNA\n(EUS-FNA)"],
["Posterior\n(Paravertebral)", "Posterior vertebral\nbody → chest wall",
"Neurogenic tumours (Schwannoma,\nNeurofibroma, Ganglioneuroma,\nNeuroblastoma, Paraganglioma)",
"CT-guided or\nEUS-FNA"],
]
story.append(tbl(comp_h, comp_r, widths=[2.8*cm, 3*cm, 6*cm, 3.4*cm]))
# 3. APPROACH FLOWCHART
story.append(h1("3. Diagnostic Algorithm / Flowchart"))
story.append(sp(4))
story.append(DrawingFlowable(make_flowchart(),
"Figure 2. Flowchart for evaluation of mediastinal mass using FNA"))
story.append(sp(6))
# 4. FNA TECHNIQUES
story.append(h1("4. FNA Techniques — Methods and Guidance"))
story.append(sp(3))
story.append(DrawingFlowable(make_fna_technique_diagram(),
"Figure 3. Line diagram comparing CT-guided FNAC (left) and EBUS-TBNA (right) for mediastinal lesions"))
story.append(sp(6))
story.append(h2("4.1 CT-guided Transthoracic FNAC"))
story.append(b("Best for: Anterior mediastinal masses and peripheral lesions accessible percutaneously"))
story.append(b("Needle: 22–25 gauge; coaxial technique allows multiple passes through single pleural puncture"))
story.append(b("Yield: 75–80% for malignancy; combined FNA + core-needle biopsy: 98% accuracy (Schwartz's)"))
story.append(b("<b>Contraindication:</b> Suspected thymoma — avoid due to risk of tumour seeding"))
story.append(b("Complications: Pneumothorax (~15–25%), minor haemorrhage; serious bleeding is rare"))
story.append(sp(4))
story.append(h2("4.2 EBUS-TBNA (Endobronchial Ultrasound-guided Transbronchial Needle Aspiration)"))
story.append(b("Curvilinear EBUS probe on bronchoscope tip — real-time visualisation of needle in target"))
story.append(b("Needle: 19 or 22-gauge; performed under sedation as ambulatory procedure"))
story.append(b("Nodal stations accessible: Upper/lower paratracheal (L2/L4R), subcarinal (L7), hilar nodes"))
story.append(b("Sensitivity for lung cancer staging: 88–93% (meta-analyses, Cummings Otolaryngology)"))
story.append(b("Sarcoidosis: sensitivity 83%, specificity 100% (Murray & Nadel's)"))
story.append(b("Advantage over mediastinoscopy: Also accesses posterior subcarinal and hilar nodes"))
story.append(b("Limitation: Core biopsy not possible with EBUS; may be inferior to surgical biopsy for lymphoma subtyping"))
story.append(sp(4))
story.append(h2("4.3 EUS-FNA (Endoscopic Ultrasound-guided FNA)"))
story.append(b("Gastroscope-based; accesses posterior subcarinal nodes and paraesophageal lesions"))
story.append(b("Complementary to EBUS — combined EBUS+EUS improves yield for mediastinal staging"))
story.append(b("Particularly useful for levels 8 (paraesophageal) and 9 (inferior pulmonary ligament)"))
story.append(sp(4))
story.append(h2("4.4 Blind TBNA (Wang Needle)"))
story.append(b("22-gauge Wang needle through bronchoscope without ultrasound guidance"))
story.append(b("Lower yield than EBUS-TBNA; largely replaced by EBUS in modern practice"))
story.append(b("Useful for subcarinal nodes in resource-limited settings"))
# 5. SPECIMEN PROCESSING
story.append(h1("5. FNA Specimen Processing and Ancillary Tests"))
story.append(sp(3))
story.append(DrawingFlowable(make_processing_diagram(),
"Figure 4. Line diagram of FNA specimen processing pipeline"))
story.append(sp(6))
proc_h = ["Technique", "Method", "Key Information Gained"]
proc_r = [
["Pap/MGG Smear", "Air-dried (MGG) or alcohol-fixed (Pap)", "Morphology, cellular detail, nuclear features"],
["Cell Block (FFPE)", "Centrifuged pellet embedded in paraffin", "Architecture + full IHC + molecular tests"],
["ROSE", "Diff-Quik stain, immediate assessment", "Adequacy confirmation; directs further sampling"],
["IHC Panel", "On cell block sections", "CD20, CD3, CD15, CD30, TTF-1, p40, CK, AFP, hCG"],
["Flow Cytometry", "Fresh sample, surface markers", "Lymphoma classification (B/T cell, clonality)"],
["Molecular Testing", "FISH, PCR, NGS on cell block", "ALK, EGFR, c-KIT, IgH rearrangement, TP53"],
]
story.append(tbl(proc_h, proc_r, widths=[3.5*cm, 4.5*cm, 7.2*cm]))
# 6. CYTOLOGIC FEATURES
story.append(h1("6. Cytologic Features of Key Mediastinal Lesions"))
story.append(sp(3))
lesion_h = ["Lesion", "Cytologic Features", "IHC / Ancillary"]
lesion_r = [
["Thymoma",
"Biphasic: Bland epithelial cells + abundant\nimmature lymphocytes\n"
"Epithelial cells: oval nuclei, mild atypia\n"
"Background rich in lymphocytes (TdT+)\n"
"NB: Avoid FNA if thymoma strongly suspected",
"CK+, CD5+ (epithelial)\nTdT+ lymphocytes\nCD20–, CD3 mix"],
["Hodgkin Lymphoma\n(Nodular Sclerosis)",
"Large Reed-Sternberg cells: bilobed 'owl-eye'\nnuclei with prominent nucleoli\n"
"Background: eosinophils, plasma cells,\nneutrophils, lymphocytes",
"CD15+, CD30+\nCD20–, CD45–\nPAX5+ (weak)"],
["Large B-cell Lymphoma",
"Large, discohesive cells with vesicular nuclei,\nprominent nucleoli\n"
"Scant cytoplasm; necrotic background\n"
"N:C ratio high",
"CD20+, CD10±\nBCL2+, BCL6+\nMYC rearrangement by FISH"],
["Germ Cell Tumour\n(Seminoma)",
"Large polygonal cells with clear/glycogen-rich\ncytoplasm, prominent nucleoli\n"
"Background lymphocytes and granulomas\n"
"Elevated serum AFP/hCG",
"PLAP+, OCT3/4+\nCD117+ (seminoma)\nAFP+ (Yolk sac)"],
["Schwannoma\n(Neurogenic)",
"Spindle cells in fascicles; wavy nuclei\n'Verocay bodies' (palisading)\n"
"Antoni A (compact) & Antoni B (loose) areas",
"S100+, SOX10+\nCD34–, SMA–"],
["Metastatic Carcinoma",
"Cohesive clusters of epithelial cells\nNuclear pleomorphism, gland formation\n"
"May show squamous, glandular, or neuroendocrine\ndifferentiation depending on primary",
"CK7/CK20 panel\nTTF-1 (lung), CDX2 (colon)\nPSA (prostate)"],
["Mediastinal Granuloma\n(TB/Sarcoid)",
"Epithelioid histiocytes, Langhans giant cells\nCaseating necrosis (TB) or non-caseating (sarcoid)\n"
"ZN stain for AFB; culture essential",
"Negative for malignancy\nZN / GMS stains\nPCR for TB"],
]
story.append(tbl(lesion_h, lesion_r, widths=[3.5*cm, 7*cm, 4.7*cm]))
# 7. ACCURACY & LIMITATIONS
story.append(h1("7. Diagnostic Accuracy and Limitations"))
story.append(sp(3))
acc_h = ["Technique", "Sensitivity", "Key Limitation"]
acc_r = [
["CT-guided FNAC", "75–80% (carcinoma/lymphoma)", "Risk of seeding in thymoma; pneumothorax"],
["FNA + Core-needle biopsy", "98% (combined)", "More invasive; requires coaxial system"],
["EBUS-TBNA (lung cancer staging)", "88–93%", "Cannot subtype lymphoma reliably"],
["EBUS-TBNA (sarcoidosis)", "83% sensitivity, 100% specificity", "Requires dedicated cytopathologist"],
["EUS-FNA", "80–90%", "Limited to posterior/paraesophageal nodes"],
["Surgical mediastinoscopy", "Near 100% (gold standard)", "Invasive; anaesthesia risk; higher cost"],
]
story.append(tbl(acc_h, acc_r, widths=[4.5*cm, 4.5*cm, 6.2*cm]))
story.append(sp(6))
story.append(h2("False Positives"))
story.append(b("Reactive lymphoid hyperplasia misread as lymphoma"))
story.append(b("Thymic hyperplasia vs thymoma (overlapping morphology)"))
story.append(b("Reactive mesothelial cells in cyst aspirates"))
story.append(h2("False Negatives"))
story.append(b("Sclerosing lymphoma — poor cellularity due to fibrosis"))
story.append(b("Sampling error in heterogeneous or necrotic masses"))
story.append(b("Well-differentiated thymoma mistaken for benign"))
# 8. COMPARISON FNA vs SURGICAL BIOPSY
story.append(h1("8. FNA vs. Surgical Biopsy — Comparative Summary"))
story.append(sp(3))
cmp_h = ["Parameter", "FNA / Core Biopsy", "Surgical Biopsy (Mediastinoscopy)"]
cmp_r = [
["Invasiveness", "Minimal", "Major; requires GA"],
["Complication rate", "Low (<5%)", "Higher (bleeding, nerve injury)"],
["Tissue amount", "Small (cells/fragments)", "Large (architecture preserved)"],
["Lymphoma subtyping", "Limited (flow cytometry helps)", "Reliable (WHO classification)"],
["Molecular testing", "Yes (on cell block)", "Yes (on histology)"],
["Turnaround time", "Hours (ROSE same day)", "Days"],
["Cost", "Low", "High"],
["Best for", "Carcinoma, SCLC, metastases,\ngerm cell, sarcoidosis", "Lymphoma subtyping,\nthymoma, equivocal FNA"],
]
story.append(tbl(cmp_h, cmp_r, widths=[3.5*cm, 5.5*cm, 6.2*cm]))
# 9. CONCLUSION
story.append(h1("9. Conclusion"))
story.append(sp(3))
story.append(body(
"FNA plays a pivotal role in the evaluation of mediastinal masses, providing a rapid, "
"safe, and minimally invasive means of tissue diagnosis. The choice of FNA technique — CT-guided, "
"EBUS-TBNA, or EUS-FNA — is guided by the compartment of origin and the clinical differential diagnosis. "
"Combining FNA with ROSE, cell block preparation, IHC, flow cytometry, and molecular testing maximises "
"diagnostic yield (up to 98% when FNA and core-needle biopsy are combined). Surgical biopsy remains "
"reserved for inconclusive FNA or when detailed lymphoma subtyping is required. In experienced hands, "
"FNA reduces the need for invasive surgery in the majority of patients with mediastinal masses."
))
story.append(sp(8))
story.append(HRFlowable(width="100%", thickness=1.5, color=BLU, spaceAfter=5))
story.append(Paragraph(
"EXAM TIP (10 Marks): Introduction (1) + Compartments + Techniques with line diagram (3) + "
"Flowchart (2) + Cytologic features of lesions (3) + Accuracy, limitations, conclusion (1). "
"Draw Figures 1–4 in the exam for full marks.",
tip_s
))
story.append(sp(4))
story.append(Paragraph(
"Sources: Murray & Nadel's Textbook of Respiratory Medicine, 2-Vol | "
"Schwartz's Principles of Surgery 11e | Goldman-Cecil Medicine | "
"Fishman's Pulmonary Diseases and Disorders",
note_s
))
doc.build(story)
print(f"PDF saved: {OUTPUT}")
cd /tmp/workspace/lrt-cytology && python generate_mediastinal_fna.py 2>&1
~/lrt-cytology/generate_mediastinal_fna.py
Successfully replaced 1 instance(s) of the string in '/tmp/workspace/lrt-cytology/generate_mediastinal_fna.py'
cd /tmp/workspace/lrt-cytology && python generate_mediastinal_fna.py 2>&1
~/lrt-cytology/generate_mediastinal_fna.py
Successfully replaced 1 instance(s) of the string in '/tmp/workspace/lrt-cytology/generate_mediastinal_fna.py'
cd /tmp/workspace/lrt-cytology && python generate_mediastinal_fna.py 2>&1
FNA Mediastinal Masses RGUHS
PDF Document · PDF
Pdf on Discuss the role of FNA in the evaluation of mediastinal masses using flowchart And line diagram for university exam
FNA Mediastinal Masses RGUHS
PDF Document · PDF