Gall Bladder Mass Cytology/FNAC
gallbladder mass FNAC cytology diagnosis
gallbladder carcinoma fine needle aspiration cytology
gallbladder mass FNAC cytology features adenocarcinoma diagnosis criteria 2024
https://pmc.ncbi.nlm.nih.gov/articles/PMC6367906
Note: FNAC is generally avoided when the lesion is potentially resectable with curative intent, to prevent peritoneal seeding.
| Method | Details |
|---|---|
| US-guided percutaneous FNAC | Most common; outpatient; 22-25G needle |
| EUS-guided FNA | Preferred for deep masses; allows concurrent staging; forward-viewing echoendoscope used especially post-Roux-en-Y reconstruction |
| CT-guided | Used when US visualization is poor |
| ERCP-based cell block | For associated biliary obstruction |
| Category | Diagnosis | Cytological Features |
|---|---|---|
| I | Inadequate | Scant cellularity, degenerated cells, blood/clot only |
| II | Benign/Reactive | Monolayered sheets, uniform chromatin, small nucleoli, mild atypia |
| III | Atypical | Hypercellular smears, cellular overlapping, mild pleomorphism, enlarged nuclei, pale cytoplasm, occasional mitosis - equivocal |
| IV | Suspicious | Features concerning but not diagnostic for malignancy |
| V | Malignant | Definitive malignant features |
| Feature | Frequency |
|---|---|
| Nuclear pleomorphism - moderate | 47.8% |
| Nuclear pleomorphism - severe | 32.8% |
| Vesicular chromatin | 59.2% |
| Coarse/irregular chromatin | 32.5% |
| Prominent nucleoli | Present |
| Mitotic figures | Variable |

| Condition | Key Distinguishing Features |
|---|---|
| Xanthogranulomatous cholecystitis (XGC) | Most common mimic; foamy histiocytes, chronic inflammation, no true malignant epithelial atypia; can show reactive atypia - major pitfall |
| Adenomatous hyperplasia / Adenoma | Monolayered sheets, uniform nuclei, small nucleoli, no high-grade atypia |
| Gallbladder tuberculosis | Epithelioid cell granulomas in necrotic background; no carcinoma cells |
| Metastatic carcinoma | Clinical history + IHC; CK7/CK20, CDX2, TTF-1 panel helps |
| Cholangiocarcinoma extending to GB | Difficult distinction cytologically; clinical/imaging correlation essential |
XGC is the most treacherous mimic - smears can be hypercellular with reactive epithelial atypia. Features favoring malignancy over XGC: severe nuclear pleomorphism, coarse chromatin, high N:C ratio, necrotic diathesis, and true glandular formations.
| Parameter | Value |
|---|---|
| Sensitivity | ~93-97% |
| Specificity | ~95-100% |
| SROC AUC | 0.98 |
| Inadequacy rate | ~9.9-11% |
| Complication rate | Minimal (bile leakage rare) |
What are the specific Bethesda-like criteria for gallbladder cytology reporting?
Papanicolaou Society cytopathology pancreatobiliary reporting system gallbladder
Papanicolaou Society cytopathology reporting system gallbladder biliary 6 categories Bethesda criteria 2023 2024
https://www.pathologyoutlines.com/topic/pancreaspapanicolaou…
pancreatobiliary cytology reporting system categories risk malignancy PSC guidelines
https://www.pathologyoutlines.com/topic/gallbladdercarcinoma…
"pancreatobiliary cytology" reporting categories atypical suspicious malignant risk
Papanicolaou Society pancreaticobiliary cytology reporting system 2014
PSC Papanicolaou Society pancreatobiliary cytology reporting system category III atypical IV neoplastic gallbladder criteria ROM
| Subtype | Key Cytological Criteria |
|---|---|
| Papillary carcinoma | True papillae with fibrovascular cores; mild atypia; fewer mitoses; better prognosis |
| Mucinous adenocarcinoma | >50% extracellular mucin on smear; poor prognosis |
| Signet ring cell | Nucleus displaced to periphery by intracytoplasmic mucin globule |
| Adenosquamous | Mixed glandular + atypical squamous (keratinized) cells |
| Squamous cell carcinoma | Atypical squamous cells, keratin pearls, necrotic background; no glands |
| Small cell (NEC) | Hyperchromatic nuclei, nuclear molding, salt-and-pepper chromatin, scant cytoplasm, rosettes absent |
| Undifferentiated (spindle/giant cell) | Pleomorphic spindle cells or bizarre multinucleate giant cells; no glandular differentiation |
| PSC (6-tier) | WHO (7-tier) | Key Change |
|---|---|---|
| I. Nondiagnostic | 1. Insufficient/Nondiagnostic | Same |
| II. Negative | 2. Benign/Negative for Malignancy | Same |
| III. Atypical | 3. Atypical | Same |
| IV. Neoplastic - Benign | 4. PanN-Low (low-risk/grade neoplasm) | BilIN-low, ICPTN low-grade |
| IV. Neoplastic - Other | 5. PanN-High (high-risk/grade neoplasm) | BilIN-high, ICPTN high-grade |
| V. Suspicious | 6. Suspicious for Malignancy | Same |
| VI. Positive/Malignant | 7. Malignant | Same |
| Category | PSC Name | WHO Name | ROM (aggregate) | Management |
|---|---|---|---|---|
| I | Nondiagnostic | Insufficient/ND | ~21-23% | Repeat FNA |
| II | Negative | Benign/NFM | ~2.5-13% | Conservative/cholecystectomy |
| III | Atypical | Atypical | 25-74% | Repeat/molecular/MDT |
| IV-a | Neoplastic: Benign | PanN-Low | ~0-14% | Follow-up or surgery |
| IV-b | Neoplastic: Other | PanN-High | ~27-92% | Surgery referral |
| V | Suspicious | Suspicious | ~75-86% | HPB surgical referral |
| VI | Positive/Malignant | Malignant | ~87-100% | Urgent HPB surgical referral |
| Category | Recommended Ancillary Tests |
|---|---|
| Cat I | CEA + amylase (cystic lesions); re-aspirate with ROSE |
| Cat III | FISH (polysomy/deletion), SMAD4 IHC loss, p53 mutation, repeat FNA |
| Cat IV-b | Ki-67 (NET grading), Synaptophysin/Chromogranin (NEC), KRAS/GNAS |
| Cat V/VI | CK7, CK20, CEA, CA19-9 on cell block; HER2 IHC/FISH (targeted therapy eligibility per PMID 37221046); TP53 sequencing |
Create a quick reference PDF comparing PSC vs WHO reporting systems for gallbladder cytology
ls /home/daytona/workspace/ 2>/dev/null || echo "workspace empty or not found"
mkdir -p /home/daytona/workspace/gb-cytology-pdf && python3 -c "from reportlab.l…
~/gb-cytology-pdf/build_pdf.py
#!/usr/bin/env python3
"""
Quick Reference PDF: PSC vs WHO Reporting Systems for Gallbladder Cytology
"""
from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.units import cm, mm
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.enums import TA_LEFT, TA_CENTER, TA_RIGHT
from reportlab.platypus import (
SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
HRFlowable, KeepTogether
)
from reportlab.platypus.flowables import HRFlowable
from reportlab.lib.colors import HexColor
OUTPUT = "/home/daytona/workspace/gb-cytology-pdf/GB_Cytology_PSC_vs_WHO.pdf"
# ── Colour palette ──────────────────────────────────────────────────────────
NAVY = HexColor("#1B3A5C")
TEAL = HexColor("#1E7A8C")
AMBER = HexColor("#D4860B")
LIGHT_BG = HexColor("#EEF4F7")
ROW_ALT = HexColor("#F5FBFC")
RED_RISK = HexColor("#C0392B")
GREEN_OK = HexColor("#1E8449")
ORANGE = HexColor("#CA6F1E")
YELLOW = HexColor("#D4AC0D")
GREY_BG = HexColor("#F2F3F4")
WHITE = colors.white
BLACK = colors.black
# ── Styles ──────────────────────────────────────────────────────────────────
styles = getSampleStyleSheet()
H_TITLE = ParagraphStyle("htitle",
fontSize=18, leading=22, textColor=WHITE,
fontName="Helvetica-Bold", alignment=TA_CENTER)
H1 = ParagraphStyle("h1",
fontSize=13, leading=16, textColor=NAVY,
fontName="Helvetica-Bold", spaceAfter=4)
H2 = ParagraphStyle("h2",
fontSize=10, leading=13, textColor=TEAL,
fontName="Helvetica-Bold", spaceAfter=2)
BODY = ParagraphStyle("body",
fontSize=8.5, leading=12, textColor=BLACK,
fontName="Helvetica", spaceAfter=2)
SMALL = ParagraphStyle("small",
fontSize=7.5, leading=10, textColor=HexColor("#444444"),
fontName="Helvetica")
CELL = ParagraphStyle("cell",
fontSize=8, leading=10.5, textColor=BLACK,
fontName="Helvetica")
CELL_B = ParagraphStyle("cellb",
fontSize=8, leading=10.5, textColor=BLACK,
fontName="Helvetica-Bold")
CELL_W = ParagraphStyle("cellw",
fontSize=8, leading=10.5, textColor=WHITE,
fontName="Helvetica-Bold", alignment=TA_CENTER)
FOOT = ParagraphStyle("foot",
fontSize=7, leading=9, textColor=HexColor("#666666"),
fontName="Helvetica-Oblique", alignment=TA_CENTER)
W = A4[0] # page width
MARGIN = 1.5 * cm
COL = W - 2 * MARGIN
def p(text, style=BODY): return Paragraph(text, style)
def sp(h=0.25): return Spacer(1, h * cm)
def hr(): return HRFlowable(width="100%", thickness=0.6, color=TEAL, spaceAfter=6, spaceBefore=6)
# ── Risk badge helper ────────────────────────────────────────────────────────
def rom_color(rom_str):
"""Return background colour for a ROM string."""
s = rom_str.lower()
if "0" in s and "%" in s and not any(c.isdigit() and int(c) > 1 for c in s.split("%")[0][-3:]):
return GREEN_OK
for val in ["2","3","4","5","6","7","8","9","10","11","12","13"]:
if s.startswith(val):
return GREEN_OK
if any(x in s for x in ["87","88","89","90","91","92","93","94","95","96","97","98","99","100"]):
return RED_RISK
if any(x in s for x in ["75","76","77","78","79","80","81","82","83","84","85","86"]):
return ORANGE
if any(x in s for x in ["25","26","27","28","29","30","40","44","50","60","62","70","71","72","73","74"]):
return AMBER
return TEAL
# ═══════════════════════════════════════════════════════════════════════════
# BUILD DOCUMENT
# ═══════════════════════════════════════════════════════════════════════════
doc = SimpleDocTemplate(
OUTPUT,
pagesize=A4,
leftMargin=MARGIN, rightMargin=MARGIN,
topMargin=1.2*cm, bottomMargin=1.5*cm,
title="PSC vs WHO Gallbladder Cytology Quick Reference",
author="Orris Clinical Reference"
)
story = []
# ── HEADER BANNER ───────────────────────────────────────────────────────────
header_data = [[
p("Gallbladder Cytology Reporting Systems\nPSC 6-Tier vs WHO 7-Tier — Quick Reference", H_TITLE)
]]
header_tbl = Table(header_data, colWidths=[COL])
header_tbl.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), NAVY),
("ROUNDEDCORNERS", [6, 6, 6, 6]),
("TOPPADDING", (0,0), (-1,-1), 10),
("BOTTOMPADDING", (0,0), (-1,-1), 10),
]))
story.append(header_tbl)
story.append(sp(0.35))
# ── SUBTITLE BAR ────────────────────────────────────────────────────────────
sub_data = [[
p("PSC = Papanicolaou Society of Cytopathology (2014) | WHO = World Health Organization Pancreaticobiliary System (2019/2022) | ROM = Risk of Malignancy",
ParagraphStyle("sub", fontSize=7.5, leading=10, textColor=WHITE, fontName="Helvetica", alignment=TA_CENTER))
]]
sub_tbl = Table(sub_data, colWidths=[COL])
sub_tbl.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), TEAL),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING", (0,0), (-1,-1), 5),
]))
story.append(sub_tbl)
story.append(sp(0.4))
# ════════════════════════════════════════════════════════════════════════════
# SECTION 1: SIDE-BY-SIDE COMPARISON TABLE
# ════════════════════════════════════════════════════════════════════════════
story.append(p("1. Category-by-Category Comparison", H1))
story.append(hr())
# Column widths: PSC#, PSC Name, WHO#, WHO Name, ROM, Management, GB Entities
CW = [COL * x for x in [0.07, 0.16, 0.07, 0.18, 0.12, 0.20, 0.20]]
col_hdr = [
p("PSC\nCat.", CELL_W),
p("PSC Name", CELL_W),
p("WHO\nCat.", CELL_W),
p("WHO Name", CELL_W),
p("ROM\n(avg.)", CELL_W),
p("Management", CELL_W),
p("GB-Specific Entities", CELL_W),
]
rows = [
# [PSC#, PSC Name, WHO#, WHO Name, ROM, Management, GB Entities]
["I", "Nondiagnostic", "1", "Insufficient /\nNondiagnostic",
"~21–23%",
"Repeat FNA; use ROSE;\nre-target with EUS/CT",
"Scant cells; obscuring\nblood/necrosis; cyst\nfluid only"],
["II", "Negative for\nMalignancy", "2", "Benign /\nNeg. for Malignancy",
"~2.5–13%",
"Conservative; laparoscopic\ncholecystectomy if indicated",
"Chronic cholecystitis;\nXGC*; adenoma;\nGB tuberculosis; cholesterol polyp"],
["III", "Atypical", "3", "Atypical",
"25–74%",
"Repeat FNA; ancillary\ntesting; MDT review;\nsurgery if high suspicion",
"XGC with reactive\natypia (main pitfall);\nreparative changes\npost-cholecystitis"],
["IV-a","Neoplastic:\nBenign", "4", "PanN-Low\n(low-risk/grade)",
"0–14%",
"Follow-up or elective\ncholecystectomy",
"Serous cystadenoma;\nICPTN† low-grade;\nBilIN‡ low-grade"],
["IV-b","Neoplastic:\nOther", "5", "PanN-High\n(high-risk/grade)",
"27–92%",
"Surgical referral;\nHPB consultation",
"ICPTN high-grade;\nBilIN high-grade;\nNET (Ki-67 grading req'd)"],
["V", "Suspicious for\nMalignancy","6", "Suspicious for\nMalignancy",
"75–86%",
"Urgent HPB referral;\ntreat as malignant for\nsurgical planning",
"Well-diff. adenocarcinoma\n(classic Cat V trap);\npartially obscured\nmalignant cells"],
["VI", "Positive /\nMalignant", "7", "Malignant",
"87–100%",
"Urgent HPB centre;\nstaging CT/MRI;\nresectability assessment",
"Adenocarcinoma (87%);\nSCC; mucinous; signet\nring; small cell;\nundifferentiated"],
]
table_data = [col_hdr]
for r in rows:
row = [p(r[0], CELL_B), p(r[1], CELL), p(r[2], CELL_B), p(r[3], CELL),
p(r[4], CELL_B), p(r[5], CELL), p(r[6], CELL)]
table_data.append(row)
tbl_style = [
# Header
("BACKGROUND", (0,0), (-1,0), NAVY),
("TEXTCOLOR", (0,0), (-1,0), WHITE),
("FONTNAME", (0,0), (-1,0), "Helvetica-Bold"),
("FONTSIZE", (0,0), (-1,0), 8),
("ALIGN", (0,0), (-1,0), "CENTER"),
("VALIGN", (0,0), (-1,-1), "TOP"),
# Borders
("INNERGRID", (0,0), (-1,-1), 0.4, HexColor("#BBBBBB")),
("BOX", (0,0), (-1,-1), 0.8, NAVY),
# Alternating rows
("BACKGROUND", (0,1), (-1,1), LIGHT_BG),
("BACKGROUND", (0,2), (-1,2), WHITE),
("BACKGROUND", (0,3), (-1,3), LIGHT_BG),
("BACKGROUND", (0,4), (-1,4), WHITE),
("BACKGROUND", (0,5), (-1,5), LIGHT_BG),
("BACKGROUND", (0,6), (-1,6), WHITE),
("BACKGROUND", (0,7), (-1,7), LIGHT_BG),
# Padding
("TOPPADDING", (0,0), (-1,-1), 4),
("BOTTOMPADDING",(0,0), (-1,-1), 4),
("LEFTPADDING", (0,0), (-1,-1), 4),
("RIGHTPADDING", (0,0), (-1,-1), 4),
# Highlight ROM column background by risk
("BACKGROUND", (4,1), (4,1), GREEN_OK), # Cat I ROM col
("BACKGROUND", (4,2), (4,2), GREEN_OK), # Cat II
("BACKGROUND", (4,3), (4,3), AMBER), # Cat III
("BACKGROUND", (4,4), (4,4), GREEN_OK), # Cat IV-a
("BACKGROUND", (4,5), (4,5), ORANGE), # Cat IV-b
("BACKGROUND", (4,6), (4,6), ORANGE), # Cat V
("BACKGROUND", (4,7), (4,7), RED_RISK), # Cat VI
("TEXTCOLOR", (4,1), (4,7), WHITE),
("ALIGN", (4,0), (4,-1), "CENTER"),
("FONTNAME", (4,1), (4,7), "Helvetica-Bold"),
]
comp_tbl = Table(table_data, colWidths=CW, repeatRows=1)
comp_tbl.setStyle(TableStyle(tbl_style))
story.append(comp_tbl)
story.append(sp(0.2))
story.append(p(
"* XGC = Xanthogranulomatous Cholecystitis (most common mimic of malignancy; foamy histiocytes + reactive atypia). "
"† ICPTN = Intracholecystic Papillary Tubular Neoplasm (GB equivalent of IPMN). "
"‡ BilIN = Biliary Intraepithelial Neoplasia (flat precursor lesion).", SMALL))
story.append(sp(0.5))
# ════════════════════════════════════════════════════════════════════════════
# SECTION 2: KEY CYTOLOGICAL CRITERIA PER CATEGORY
# ════════════════════════════════════════════════════════════════════════════
story.append(p("2. Key Cytological Criteria per Reporting Category (Gallbladder)", H1))
story.append(hr())
criteria = [
("Cat I — Nondiagnostic", LIGHT_BG, [
("Adequacy failure:", "Scant biliary epithelium; degenerated cells; obscuring blood/necrosis/clot"),
("Cystic lesion:", "Acellular fluid with no mucinous features (no mucin, normal CEA, no KRAS/GNAS)"),
("Pitfall:", "DO NOT diagnose XGC or chronic cholecystitis from scant cells — call nondiagnostic"),
("Action:", "Repeat FNA; ROSE reduces Cat I rate significantly"),
]),
("Cat II — Negative for Malignancy", LIGHT_BG, [
("Architecture:", "Monolayered honeycomb sheets; preserved polarity; flat tissue fragments"),
("Nuclear features:", "Uniform size (1–2× RBC); smooth membranes; fine chromatin; small/absent nucleoli"),
("Cytoplasm:", "Abundant pale/granular; low N:C ratio; no intracellular mucin"),
("Background:", "Bile pigment; foamy histiocytes; occasional inflammatory cells"),
("XGC pattern:", "Sheets of foamy macrophages + reactive epithelial atypia WITHOUT high-grade features"),
("NPV caution:", "Only ~71% — negative result does NOT exclude carcinoma in suspicious mass"),
]),
("Cat III — Atypical (Most Heterogeneous)", HexColor("#FEF9E7"), [
("Threshold:", "Quantitatively insufficient for Suspicious, but qualitatively beyond benign"),
("Architecture:", "Hypercellular; cellular overlapping; mild 3D crowding; no clear glandular formations"),
("Nuclear features:", "Mild–moderate pleomorphism; enlarged nuclei (2–3× normal); mostly uniform chromatin"),
("Cytoplasm:", "Pale; vacuolated; slightly elevated N:C ratio"),
("Mitoses:", "Occasional; not atypical"),
("Background:", "May be inflammatory; no necrotic diathesis"),
("ROM range:", "25–74% — widest variance across studies; always requires follow-up"),
]),
("Cat IV-b / PanN-High — Neoplastic (Other)", LIGHT_BG, [
("ICPTN (high-grade):", "Papillary clusters with fibrovascular cores; columnar cells; moderate atypia"),
("BilIN high-grade:", "Typically from biliary brushings; high-grade dysplasia without invasive features"),
("NET:", "Rosettes/trabeculae; salt-and-pepper chromatin; plasmacytoid cells; low mitotic activity; positive Synaptophysin/Chromogranin; Ki-67 determines grade"),
("ROM:", "27–92% — overlaps with suspicious; surgical referral usually indicated"),
]),
("Cat V — Suspicious for Malignancy", HexColor("#FEF0E6"), [
("Threshold:", "Malignant-appearing cells present but quantitatively insufficient for definitive diagnosis"),
("Architecture:", "3D clusters; pseudoglandular formations; papillary fragments"),
("Nuclear features:", "High N:C ratio; irregular nuclear membranes; coarse/clumped chromatin"),
("Nucleoli:", "Prominent macronucleoli"),
("Background:", "Necrotic diathesis present but obscures cells"),
("Classic trap:", "Well-differentiated GB adenocarcinoma — glands look near-normal; classify Suspicious not Cat II"),
]),
("Cat VI — Positive/Malignant (Unequivocal)", HexColor("#FDEDEC"), [
("Adenocarcinoma (87%):", "Sheets/acini/papillae of columnar cells; mod–severe pleomorphism; coarse chromatin; macronucleoli; necrotic background"),
("Papillary Ca:", "True papillae with fibrovascular cores; mild atypia; fewer mitoses; better prognosis"),
("Mucinous Ca:", ">50% extracellular mucin; poor prognosis; signet ring cells possible"),
("SCC:", "Atypical squamous cells; keratin pearls; no glandular component; necrotic background"),
("Small cell NEC:", "Nuclear molding; hyperchromatic nuclei; scant cytoplasm; salt-and-pepper chromatin"),
("Undifferentiated:", "Pleomorphic spindle/giant cells; no glandular differentiation; extremely aggressive"),
]),
]
for title, bg, items in criteria:
block = []
block.append(p(title, H2))
rows_c = [[p(k, CELL_B), p(v, CELL)] for k, v in items]
inner = Table(rows_c, colWidths=[COL*0.22, COL*0.78])
inner.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), bg),
("INNERGRID", (0,0), (-1,-1), 0.3, HexColor("#CCCCCC")),
("BOX", (0,0), (-1,-1), 0.5, TEAL),
("TOPPADDING", (0,0), (-1,-1), 3),
("BOTTOMPADDING",(0,0), (-1,-1), 3),
("LEFTPADDING", (0,0), (-1,-1), 5),
("RIGHTPADDING", (0,0), (-1,-1), 5),
("VALIGN", (0,0), (-1,-1), "TOP"),
]))
block.append(inner)
block.append(sp(0.25))
story.append(KeepTogether(block))
# ════════════════════════════════════════════════════════════════════════════
# SECTION 3: ROM SUMMARY + ANCILLARY TESTS
# ════════════════════════════════════════════════════════════════════════════
story.append(p("3. Risk of Malignancy (ROM) by Study & Ancillary Tests", H1))
story.append(hr())
# ROM table
rom_header = [p("Category", CELL_W), p("Layfield\net al.", CELL_W), p("Sung\net al.", CELL_W),
p("Hoda\net al.", CELL_W), p("Avg.\nROM", CELL_W), p("Ancillary Tests\n(Gallbladder)", CELL_W)]
rom_rows = [
["I — Nondiagnostic", "21%", "4.5%", "7.7%", "~21–23%",
"CEA + amylase (cystic); repeat with ROSE"],
["II — Negative", "13%", "2.5%", "1.0%", "~2.5–13%",
"None routine; IHC if repeat shows atypia"],
["III — Atypical", "74%", "25.3%", "28.0%", "25–74%",
"FISH (polysomy/deletion); SMAD4 IHC; p53 mutation; repeat FNA"],
["IV-a — Neo: Benign", "14%", "0%", "0%", "~0%",
"CEA cyst fluid; MUC stains"],
["IV-b — Neo: Other", "14%", "27.1%", "30.3%", "~27–92%",
"Synaptophysin/Chromogranin; Ki-67; KRAS/GNAS"],
["V — Suspicious", "82%", "75%", "100%", "75–86%",
"Cell block IHC; CK7/CK20; CEA; CA19-9"],
["VI — Malignant", "97%", "87.9%", "100%", "87–100%",
"HER2 IHC/FISH (targeted Rx); NGS; TP53; EGFR"],
]
rom_cw = [COL * x for x in [0.24, 0.10, 0.10, 0.10, 0.12, 0.34]]
rom_data = [rom_header]
for i, r in enumerate(rom_rows):
row = [p(r[0], CELL_B if i in [0,1,2,3,4,5,6] else CELL)]
for val in r[1:4]:
row.append(p(val, ParagraphStyle("romv", fontSize=8, leading=10, textColor=WHITE,
fontName="Helvetica-Bold", alignment=TA_CENTER)))
row.append(p(r[4], ParagraphStyle("romavg", fontSize=8, leading=10, textColor=WHITE,
fontName="Helvetica-Bold", alignment=TA_CENTER)))
row.append(p(r[5], CELL))
rom_data.append(row)
rom_bg = [GREEN_OK, GREEN_OK, AMBER, GREEN_OK, ORANGE, ORANGE, RED_RISK]
rom_tbl_style = [
("BACKGROUND", (0,0), (-1,0), NAVY),
("TEXTCOLOR", (0,0), (-1,0), WHITE),
("FONTNAME", (0,0), (-1,0), "Helvetica-Bold"),
("FONTSIZE", (0,0), (-1,0), 8),
("ALIGN", (0,0), (-1,0), "CENTER"),
("VALIGN", (0,0), (-1,-1), "TOP"),
("INNERGRID", (0,0), (-1,-1), 0.4, HexColor("#BBBBBB")),
("BOX", (0,0), (-1,-1), 0.8, NAVY),
("TOPPADDING", (0,0), (-1,-1), 4),
("BOTTOMPADDING",(0,0), (-1,-1), 4),
("LEFTPADDING", (0,0), (-1,-1), 4),
("RIGHTPADDING", (0,0), (-1,-1), 4),
]
for i, bg in enumerate(rom_bg):
row_i = i + 1
for col_j in [1, 2, 3, 4]:
rom_tbl_style.append(("BACKGROUND", (col_j, row_i), (col_j, row_i), bg))
rom_tbl = Table(rom_data, colWidths=rom_cw, repeatRows=1)
rom_tbl.setStyle(TableStyle(rom_tbl_style))
story.append(rom_tbl)
story.append(sp(0.5))
# ════════════════════════════════════════════════════════════════════════════
# SECTION 4: PITFALLS & CLINICAL PEARLS
# ════════════════════════════════════════════════════════════════════════════
story.append(p("4. Key Pitfalls & Clinical Pearls (Gallbladder-Specific)", H1))
story.append(hr())
pearls = [
("⚠ XGC is the #1 mimic",
"Xanthogranulomatous cholecystitis produces foamy histiocytes + reactive epithelial atypia that can mimic Cat III–V. "
"Features favouring XGC: monolayered reactive sheets, uniform chromatin, no coarse irregularity, macrophage-predominant background."),
("⚠ Well-Diff. Adenocarcinoma Trap",
"Well-differentiated GB adenocarcinoma may show minimal atypia on FNAC (Cat II or III) because desmoplastic stroma "
"and perineural invasion are architectural features not sampled by FNA. Always correlate with imaging."),
("⚠ Negative NPV ~71%",
"A Category II (Negative) result does NOT exclude carcinoma in a radiologically suspicious GB mass. "
"~30% of GB carcinomas are false-negative on FNAC. Clinical + radiological suspicion overrides cytology."),
("⚠ Avoid FNA in Resectable Disease",
"FNAC of a GB mass that is potentially resectable with curative intent carries a risk of peritoneal seeding. "
"Reserve FNA for advanced/unresectable lesions or when tissue confirmation is needed before palliative chemotherapy."),
("✔ Repeat if Category I or III",
"Inadequate (Cat I) or atypical (Cat III) results should prompt repeat FNA within 1 month, especially with "
"strong clinicoradiological suspicion. 76.9% of re-aspirated cases in one series were positive for adenocarcinoma."),
("✔ ROSE Reduces Cat I Rate",
"Rapid On-Site Evaluation (ROSE) by a cytopathologist during EUS/US-guided FNA significantly reduces the "
"nondiagnostic rate and guides needle re-passes to representative areas of the mass."),
("✔ HER2 Testing on FNA Material",
"HER2/ERBB2 overexpression occurs in >60% of GB carcinomas. IHC and FISH can be performed directly on "
"cell block material from FNA (validated 2024, J Clin Pathol PMID 37221046) — relevant for targeted therapy eligibility."),
("✔ WHO vs PSC: Practical Difference",
"The main practical upgrade of WHO 7-tier over PSC 6-tier is splitting Cat IV into PanN-Low (ROM ~0%, watch) "
"and PanN-High (ROM ~27–92%, surgery). Both systems use the same Cat I–III and Cat V–VI criteria for GB masses."),
]
pearl_rows = [[p(t, ParagraphStyle("pt", fontSize=8, leading=11, fontName="Helvetica-Bold",
textColor=NAVY if t.startswith("✔") else HexColor("#7B241C"))),
p(d, CELL)] for t, d in pearls]
pearl_tbl = Table(pearl_rows, colWidths=[COL*0.28, COL*0.72])
pearl_tbl.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), GREY_BG),
("ROWBACKGROUNDS",(0,0), (-1,-1), [WHITE, LIGHT_BG]),
("INNERGRID", (0,0), (-1,-1), 0.3, HexColor("#CCCCCC")),
("BOX", (0,0), (-1,-1), 0.6, NAVY),
("VALIGN", (0,0), (-1,-1), "TOP"),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING",(0,0), (-1,-1), 5),
("LEFTPADDING", (0,0), (-1,-1), 6),
("RIGHTPADDING", (0,0), (-1,-1), 6),
]))
story.append(pearl_tbl)
story.append(sp(0.4))
# ════════════════════════════════════════════════════════════════════════════
# SECTION 5: WHO 7-TIER EXPANDED DETAIL
# ════════════════════════════════════════════════════════════════════════════
story.append(p("5. WHO 7-Tier System — What's New vs. PSC (Gallbladder Focus)", H1))
story.append(hr())
who_data = [
[p("WHO Category", CELL_W), p("Key Change from PSC", CELL_W), p("GB-Specific Lesions", CELL_W), p("Clinical Impact", CELL_W)]
]
who_rows = [
["1. Insufficient/ND", "Same as PSC Cat I", "Same", "No change"],
["2. Benign/NFM", "Same as PSC Cat II", "Same", "No change"],
["3. Atypical", "Same as PSC Cat III; acknowledges wider ROM range (28–61% for bile duct)", "XGC reactive atypia; reparative changes", "MDT + molecular"],
["4. PanN-Low\n(PSC IV-a Benign)",
"Renamed; now includes ALL low-grade neoplasms with formally low ROM (~0–14%)",
"ICPTN low-grade; BilIN low-grade;\nserous cystadenoma",
"Conservative follow-up; elective cholecystectomy"],
["5. PanN-High\n(PSC IV-b Other)",
"NEW SEPARATE TIER — high-grade precursors with ROM 27–92%; triggers surgical urgency",
"ICPTN high-grade; BilIN high-grade;\nNET (grade-dependent)",
"Surgical referral to HPB centre"],
["6. Suspicious", "Same as PSC Cat V", "Well-diff. adenocarcinoma trap", "Urgent HPB referral"],
["7. Malignant", "Same as PSC Cat VI", "Adenocarcinoma; SCC; NEC; undiff.", "Urgent HPB centre + staging"],
]
for r in who_rows:
who_data.append([p(r[0], CELL_B), p(r[1], CELL), p(r[2], CELL), p(r[3], CELL)])
who_cw = [COL*x for x in [0.18, 0.28, 0.28, 0.26]]
who_tbl = Table(who_data, colWidths=who_cw, repeatRows=1)
who_tbl.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,0), NAVY),
("TEXTCOLOR", (0,0), (-1,0), WHITE),
("FONTNAME", (0,0), (-1,0), "Helvetica-Bold"),
("FONTSIZE", (0,0), (-1,0), 8),
("ALIGN", (0,0), (-1,0), "CENTER"),
("VALIGN", (0,0), (-1,-1), "TOP"),
("BACKGROUND", (0,4), (-1,4), HexColor("#FEF9E7")), # highlight PanN-High
("BACKGROUND", (0,3), (-1,3), LIGHT_BG),
("INNERGRID", (0,0), (-1,-1), 0.4, HexColor("#BBBBBB")),
("BOX", (0,0), (-1,-1), 0.8, NAVY),
("TOPPADDING", (0,0), (-1,-1), 4),
("BOTTOMPADDING",(0,0), (-1,-1), 4),
("LEFTPADDING", (0,0), (-1,-1), 5),
("RIGHTPADDING", (0,0), (-1,-1), 5),
]))
story.append(who_tbl)
story.append(sp(0.5))
# ── FOOTER ───────────────────────────────────────────────────────────────────
story.append(HRFlowable(width="100%", thickness=0.6, color=TEAL))
story.append(sp(0.1))
story.append(p(
"Sources: PSC System — Papanicolaou Society of Cytopathology 2014 Guidelines; Saieg & Pitman, Diagn Cytopathol 2020 (PMID 32031332); "
"Sung et al. Cancer Cytopathol 2020 (PMID 31722125) | WHO System — WHO Pancreaticobiliary Cytopathology 2019/2022; "
"JCTP 2024 (PMC11969574) | GB Cytology — PMC6367906; CytoJournal WHO Classification on FNA Material; "
"Verma et al. J Clin Pathol 2024 (PMID 37221046) | Robbins Pathology 10e p.814\n"
"Quick Reference compiled by Orris Clinical AI · July 2026 · For educational use only. Verify criteria against current institutional guidelines.",
FOOT))
# ── BUILD PDF ─────────────────────────────────────────────────────────────────
doc.build(story)
print(f"PDF created: {OUTPUT}")
cd /home/daytona/workspace/gb-cytology-pdf && python3 build_pdf.py 2>&1
ls -lh /home/daytona/workspace/gb-cytology-pdf/GB_Cytology_PSC_vs_WHO.pdf
GB Cytology PSC vs WHO
PDF Document · PDF
Unable to open
python3 -c " from pypdf import PdfReader r = PdfReader('/home/daytona/workspace/…
cp /home/daytona/workspace/gb-cytology-pdf/GB_Cytology_PSC_vs_WHO.pdf /home/dayt…
GB Cytology PSC vs WHO
PDF Document · PDF
.pptx
~/gb-cytology-pdf/build_pptx.js
const pptxgen = require("pptxgenjs");
const pres = new pptxgen();
pres.layout = "LAYOUT_WIDE"; // 13.3" x 7.5"
pres.title = "GB Cytology: PSC vs WHO Reporting Systems";
pres.author = "Orris Clinical Reference";
// ── Palette ──────────────────────────────────────────────────────────────
const NAVY = "1B3A5C";
const TEAL = "1E7A8C";
const AMBER = "D4860B";
const RED = "C0392B";
const ORANGE = "CA6F1E";
const GREEN = "1E8449";
const WHITE = "FFFFFF";
const LGREY = "EEF4F7";
const MGREY = "CCCCCC";
const BLACK = "222222";
const YELLOW = "F9E79F";
// ── Helpers ───────────────────────────────────────────────────────────────
const W = 13.3, H = 7.5;
function titleBar(slide, text, sub = "") {
// Dark banner top
slide.addShape(pres.shapes.RECTANGLE, { x: 0, y: 0, w: W, h: 1.1, fill: { color: NAVY }, line: { color: NAVY } });
slide.addText(text, { x: 0.3, y: 0.1, w: W - 0.6, h: 0.65, fontSize: 22, bold: true, color: WHITE, fontFace: "Calibri", valign: "middle" });
if (sub) slide.addText(sub, { x: 0.3, y: 0.72, w: W - 0.6, h: 0.35, fontSize: 10, color: "A8D4DF", fontFace: "Calibri", italic: true });
// Teal accent strip
slide.addShape(pres.shapes.RECTANGLE, { x: 0, y: 1.1, w: W, h: 0.08, fill: { color: TEAL }, line: { color: TEAL } });
}
function footerBar(slide, page) {
slide.addShape(pres.shapes.RECTANGLE, { x: 0, y: H - 0.32, w: W, h: 0.32, fill: { color: NAVY }, line: { color: NAVY } });
slide.addText("PSC vs WHO Gallbladder Cytology Reporting · Orris Clinical Reference · July 2026 · Educational Use Only", {
x: 0.2, y: H - 0.30, w: W - 1.2, h: 0.28, fontSize: 7, color: "A8D4DF", fontFace: "Calibri"
});
slide.addText(`${page}`, { x: W - 0.8, y: H - 0.30, w: 0.6, h: 0.28, fontSize: 8, color: WHITE, align: "right", fontFace: "Calibri" });
}
function romBadge(slide, x, y, w, h, rom, cat) {
const colors = { green: GREEN, amber: AMBER, orange: ORANGE, red: RED, teal: TEAL };
let bg = TEAL;
if (cat <= 2) bg = GREEN;
else if (cat === 3) bg = AMBER;
else if (cat === 4) bg = ORANGE;
else if (cat >= 5) bg = RED;
slide.addShape(pres.shapes.ROUNDED_RECTANGLE, { x, y, w, h, fill: { color: bg }, line: { color: bg }, rectRadius: 0.06 });
slide.addText(rom, { x, y, w, h, fontSize: 8, bold: true, color: WHITE, align: "center", valign: "middle", fontFace: "Calibri" });
}
// ════════════════════════════════════════════════════════════════════════
// SLIDE 1 – Title / Overview
// ════════════════════════════════════════════════════════════════════════
{
const s = pres.addSlide();
// Full dark bg
s.addShape(pres.shapes.RECTANGLE, { x: 0, y: 0, w: W, h: H, fill: { color: NAVY }, line: { color: NAVY } });
// Teal accent side bar
s.addShape(pres.shapes.RECTANGLE, { x: 0, y: 0, w: 0.18, h: H, fill: { color: TEAL }, line: { color: TEAL } });
// Title
s.addText("Gallbladder Cytology", { x: 0.5, y: 1.0, w: 12.3, h: 1.1, fontSize: 40, bold: true, color: WHITE, fontFace: "Calibri", charSpacing: 1 });
s.addText("Reporting Systems Quick Reference", { x: 0.5, y: 2.0, w: 12.3, h: 0.85, fontSize: 30, color: "A8D4DF", fontFace: "Calibri" });
// Divider
s.addShape(pres.shapes.RECTANGLE, { x: 0.5, y: 3.0, w: 8, h: 0.06, fill: { color: TEAL }, line: { color: TEAL } });
// System labels
s.addText([
{ text: "PSC ", options: { bold: true, color: WHITE } },
{ text: "Papanicolaou Society of Cytopathology 6-Tier System (2014)", options: { color: "CCDDEE" } }
], { x: 0.5, y: 3.3, w: 12, h: 0.45, fontSize: 13, fontFace: "Calibri" });
s.addText([
{ text: "WHO ", options: { bold: true, color: WHITE } },
{ text: "World Health Organization 7-Tier Pancreaticobiliary System (2019/2022)", options: { color: "CCDDEE" } }
], { x: 0.5, y: 3.8, w: 12, h: 0.45, fontSize: 13, fontFace: "Calibri" });
s.addText([
{ text: "ROM ", options: { bold: true, color: WHITE } },
{ text: "Risk of Malignancy per category", options: { color: "CCDDEE" } }
], { x: 0.5, y: 4.3, w: 12, h: 0.45, fontSize: 13, fontFace: "Calibri" });
// Slides outline
s.addShape(pres.shapes.RECTANGLE, { x: 0.5, y: 5.1, w: 12.3, h: 0.06, fill: { color: "2E86AB" }, line: { color: "2E86AB" } });
const topics = ["Slide 2: Category Comparison", "Slide 3: Cytological Criteria", "Slide 4: ROM Data & Ancillary Tests", "Slide 5: Pitfalls & Pearls", "Slide 6: WHO vs PSC — What's New"];
topics.forEach((t, i) => {
s.addText(`${t}`, { x: 0.6, y: 5.25 + i * 0.36, w: 11, h: 0.32, fontSize: 11, color: "A8D4DF", fontFace: "Calibri" });
});
footerBar(s, "1 / 6");
}
// ════════════════════════════════════════════════════════════════════════
// SLIDE 2 – Side-by-Side Category Comparison
// ════════════════════════════════════════════════════════════════════════
{
const s = pres.addSlide();
s.addShape(pres.shapes.RECTANGLE, { x: 0, y: 0, w: W, h: H, fill: { color: "F7FAFC" }, line: { color: "F7FAFC" } });
titleBar(s, "Category-by-Category Comparison", "PSC 6-Tier vs WHO 7-Tier — Gallbladder Mass FNAC");
// Table header
const hY = 1.25;
const cols = [
{ x: 0.15, w: 0.55, label: "PSC" },
{ x: 0.72, w: 1.45, label: "PSC Name" },
{ x: 2.19, w: 0.55, label: "WHO" },
{ x: 2.76, w: 1.65, label: "WHO Name" },
{ x: 4.43, w: 1.05, label: "ROM" },
{ x: 5.50, w: 3.60, label: "Management" },
{ x: 9.12, w: 3.95, label: "GB-Specific Entities" },
];
cols.forEach(c => {
s.addShape(pres.shapes.RECTANGLE, { x: c.x, y: hY, w: c.w, h: 0.38, fill: { color: NAVY }, line: { color: NAVY } });
s.addText(c.label, { x: c.x, y: hY, w: c.w, h: 0.38, fontSize: 9, bold: true, color: WHITE, align: "center", valign: "middle", fontFace: "Calibri" });
});
const rows = [
{ psc: "I", pscName: "Nondiagnostic", who: "1", whoName: "Insufficient / Nondiagnostic", rom: "~21–23%", romCat: 1, mgmt: "Repeat FNA; ROSE; re-target EUS/CT", entities: "Scant cells; obscuring blood/necrosis; acellular cyst fluid", bg: "FFFFFF" },
{ psc: "II", pscName: "Negative for Malignancy", who: "2", whoName: "Benign / Neg. for Malignancy", rom: "~2.5–13%",romCat: 1, mgmt: "Conservative; laparoscopic cholecystectomy if indicated", entities: "Chronic cholecystitis; XGC; adenoma; GB TB; cholesterol polyp", bg: LGREY },
{ psc: "III", pscName: "Atypical", who: "3", whoName: "Atypical", rom: "25–74%", romCat: 3, mgmt: "Repeat FNA; ancillary tests (FISH/SMAD4); MDT review", entities: "XGC with reactive atypia (main pitfall); reparative post-cholecystitis", bg: "FFFFFF" },
{ psc: "IV-a", pscName: "Neoplastic: Benign", who: "4", whoName: "PanN-Low (low-risk/grade)", rom: "0–14%", romCat: 1, mgmt: "Follow-up or elective cholecystectomy", entities: "Serous cystadenoma; ICPTN low-grade; BilIN low-grade", bg: LGREY },
{ psc: "IV-b", pscName: "Neoplastic: Other", who: "5", whoName: "PanN-High (high-risk/grade) ★NEW", rom: "27–92%", romCat: 4, mgmt: "HPB surgical referral; ancillary testing", entities: "ICPTN high-grade; BilIN high-grade; NET (Ki-67 grading)", bg: "FFFBE6" },
{ psc: "V", pscName: "Suspicious for Malignancy",who: "6", whoName: "Suspicious for Malignancy", rom: "75–86%", romCat: 4, mgmt: "Urgent HPB referral; treat as malignant for surgical planning", entities: "Well-diff. adenocarcinoma (Cat V trap); partially obscured cells", bg: "FFF0E6" },
{ psc: "VI", pscName: "Positive / Malignant", who: "7", whoName: "Malignant", rom: "87–100%", romCat: 5, mgmt: "Urgent HPB centre; staging CT/MRI; resectability assessment", entities: "Adenocarcinoma (87%); SCC; mucinous; signet ring; small cell; undifferentiated", bg: "FDECEA" },
];
const rowH = 0.72;
rows.forEach((r, i) => {
const y = hY + 0.38 + i * rowH;
// Row bg
s.addShape(pres.shapes.RECTANGLE, { x: 0.15, y, w: W - 0.3, h: rowH - 0.03, fill: { color: r.bg }, line: { color: MGREY, width: 0.4 } });
s.addText(r.psc, { x: cols[0].x + 0.02, y, w: cols[0].w - 0.04, h: rowH - 0.03, fontSize: 9, bold: true, color: NAVY, align: "center", valign: "middle", fontFace: "Calibri" });
s.addText(r.pscName,{ x: cols[1].x + 0.04, y, w: cols[1].w - 0.08, h: rowH - 0.03, fontSize: 8.5, bold: false, color: BLACK, valign: "middle", fontFace: "Calibri" });
s.addText(r.who, { x: cols[2].x + 0.02, y, w: cols[2].w - 0.04, h: rowH - 0.03, fontSize: 9, bold: true, color: TEAL, align: "center", valign: "middle", fontFace: "Calibri" });
s.addText(r.whoName,{ x: cols[3].x + 0.04, y, w: cols[3].w - 0.08, h: rowH - 0.03, fontSize: 8.5, color: BLACK, valign: "middle", fontFace: "Calibri" });
// ROM badge
const romBg = r.romCat <= 1 ? GREEN : r.romCat === 3 ? AMBER : r.romCat === 4 ? ORANGE : RED;
s.addShape(pres.shapes.ROUNDED_RECTANGLE, { x: cols[4].x + 0.05, y: y + 0.18, w: cols[4].w - 0.10, h: rowH - 0.40, fill: { color: romBg }, line: { color: romBg }, rectRadius: 0.05 });
s.addText(r.rom, { x: cols[4].x + 0.05, y: y + 0.18, w: cols[4].w - 0.10, h: rowH - 0.40, fontSize: 8, bold: true, color: WHITE, align: "center", valign: "middle", fontFace: "Calibri" });
s.addText(r.mgmt, { x: cols[5].x + 0.04, y, w: cols[5].w - 0.08, h: rowH - 0.03, fontSize: 8, color: BLACK, valign: "middle", fontFace: "Calibri" });
s.addText(r.entities,{x: cols[6].x + 0.04, y, w: cols[6].w - 0.08, h: rowH - 0.03, fontSize: 7.5, color: BLACK, valign: "middle", fontFace: "Calibri" });
// Vertical grid lines
[cols[1].x, cols[2].x, cols[3].x, cols[4].x, cols[5].x, cols[6].x].forEach(lx => {
s.addShape(pres.shapes.RECTANGLE, { x: lx, y, w: 0.01, h: rowH - 0.03, fill: { color: MGREY }, line: { color: MGREY } });
});
});
// Legend
s.addText("★ NEW = WHO-only category (split from PSC Cat IV) | XGC = Xanthogranulomatous Cholecystitis | ICPTN = Intracholecystic Papillary Tubular Neoplasm | BilIN = Biliary Intraepithelial Neoplasia", {
x: 0.15, y: H - 0.62, w: W - 0.3, h: 0.28, fontSize: 7, color: "555555", fontFace: "Calibri", italic: true
});
footerBar(s, "2 / 6");
}
// ════════════════════════════════════════════════════════════════════════
// SLIDE 3 – Cytological Criteria
// ════════════════════════════════════════════════════════════════════════
{
const s = pres.addSlide();
s.addShape(pres.shapes.RECTANGLE, { x: 0, y: 0, w: W, h: H, fill: { color: "F7FAFC" }, line: { color: "F7FAFC" } });
titleBar(s, "Key Cytological Criteria per Category", "Gallbladder Mass FNAC — Cytomorphological Features");
// 6 cards, 2 rows × 3 cols
const cards = [
{
cat: "Cat I — Nondiagnostic", bg: "EAF4F7", border: TEAL,
items: [
["Adequacy failure:", "Scant biliary epithelium; degenerated/lysed cells; obscuring blood, clot or necrotic debris"],
["Cystic lesion:", "Acellular fluid with no mucin, normal CEA, no KRAS/GNAS mutations"],
["Action:", "Repeat FNA with ROSE; EUS-guided re-targeting to viable tumour edge"],
]
},
{
cat: "Cat II — Negative for Malignancy", bg: "EAF7EE", border: GREEN,
items: [
["Architecture:", "Monolayered honeycomb sheets; flat fragments; preserved polarity"],
["Nuclei:", "Uniform; smooth membranes; fine chromatin; small/absent nucleoli; low N:C"],
["Background:", "Bile pigment; foamy histiocytes; sparse inflammation"],
["⚠ NPV ~71%:", "Negative result does NOT exclude carcinoma in suspicious mass"],
]
},
{
cat: "Cat III — Atypical (Widest Variance)", bg: "FEF9E7", border: AMBER,
items: [
["Threshold:", "Qualitatively beyond benign but quantitatively below Suspicious"],
["Nuclear features:", "Mild–moderate pleomorphism; enlarged nuclei (2–3× normal); mostly uniform chromatin"],
["Architecture:", "Hypercellular; overlapping; no clear glandular formations; occasional mitoses"],
["ROM:", "25–74% — widest range; always requires repeat or MDT review"],
]
},
{
cat: "Cat IV-b / PanN-High — Neoplastic: Other", bg: "FEF0E6", border: ORANGE,
items: [
["ICPTN high-grade:", "Papillary clusters with fibrovascular cores; columnar cells; moderate atypia"],
["BilIN high-grade:", "High-grade dysplasia without invasive features (biliary brushings)"],
["NET:", "Rosettes/trabeculae; salt-and-pepper chromatin; plasmacytoid; low mitoses; Synaptophysin+"],
["ROM:", "27–92% — surgical referral usually indicated"],
]
},
{
cat: "Cat V — Suspicious for Malignancy", bg: "FDE8E8", border: ORANGE,
items: [
["Architecture:", "3D clusters; pseudoglandular formations; papillary fragments"],
["Nuclei:", "High N:C; irregular membranes; coarse/clumped chromatin; macronucleoli"],
["Background:", "Necrotic diathesis — partially obscures malignant cells"],
["Classic trap:", "Well-diff. GB adenocarcinoma: near-normal glands on cytology; classify Suspicious, NOT Cat II"],
]
},
{
cat: "Cat VI — Positive / Malignant", bg: "FDEDEC", border: RED,
items: [
["Adenocarcinoma (87%):", "Sheets/acini/papillae; coarse chromatin; macronucleoli; necrotic background"],
["Mucinous Ca:", ">50% extracellular mucin; poor prognosis; signet ring cells possible"],
["Small cell NEC:", "Nuclear molding; hyperchromatic; salt-and-pepper chromatin; scant cytoplasm"],
["SCC / Undiff.:", "Keratin pearls / bizarre giant or spindle cells; no glandular component"],
]
},
];
const cardW = (W - 0.45) / 3;
const cardH = 2.7;
const startY = 1.25;
cards.forEach((c, i) => {
const col = i % 3;
const row = Math.floor(i / 3);
const x = 0.15 + col * (cardW + 0.075);
const y = startY + row * (cardH + 0.15);
s.addShape(pres.shapes.RECTANGLE, { x, y, w: cardW, h: cardH, fill: { color: c.bg }, line: { color: c.border, width: 1.2 }, shadow: { type: "outer", color: "000000", blur: 4, offset: 2, angle: 135, opacity: 0.10 } });
// Header strip
s.addShape(pres.shapes.RECTANGLE, { x, y, w: cardW, h: 0.35, fill: { color: c.border }, line: { color: c.border } });
s.addText(c.cat, { x: x + 0.05, y, w: cardW - 0.1, h: 0.35, fontSize: 8.5, bold: true, color: WHITE, valign: "middle", fontFace: "Calibri" });
let itemY = y + 0.42;
c.items.forEach(([label, val]) => {
s.addText([
{ text: label + " ", options: { bold: true, color: NAVY } },
{ text: val, options: { color: BLACK } }
], { x: x + 0.08, y: itemY, w: cardW - 0.16, h: 0.52, fontSize: 7.5, fontFace: "Calibri", valign: "top" });
itemY += 0.54;
});
});
footerBar(s, "3 / 6");
}
// ════════════════════════════════════════════════════════════════════════
// SLIDE 4 – ROM Data & Ancillary Tests
// ════════════════════════════════════════════════════════════════════════
{
const s = pres.addSlide();
s.addShape(pres.shapes.RECTANGLE, { x: 0, y: 0, w: W, h: H, fill: { color: "F7FAFC" }, line: { color: "F7FAFC" } });
titleBar(s, "Risk of Malignancy (ROM) by Study & Ancillary Tests", "Aggregate data from Layfield et al., Sung et al., and Hoda et al.");
// ROM Table
const tblY = 1.3;
const tblCols = [
{ x: 0.15, w: 2.60, label: "Category" },
{ x: 2.77, w: 1.10, label: "Layfield" },
{ x: 3.89, w: 1.10, label: "Sung" },
{ x: 5.01, w: 1.10, label: "Hoda" },
{ x: 6.13, w: 1.20, label: "Avg. ROM" },
{ x: 7.35, w: 5.75, label: "Ancillary Tests (Gallbladder-Specific)" },
];
// Header
tblCols.forEach(c => {
s.addShape(pres.shapes.RECTANGLE, { x: c.x, y: tblY, w: c.w, h: 0.38, fill: { color: NAVY }, line: { color: NAVY } });
s.addText(c.label, { x: c.x, y: tblY, w: c.w, h: 0.38, fontSize: 9, bold: true, color: WHITE, align: "center", valign: "middle", fontFace: "Calibri" });
});
const romRows = [
{ cat: "I — Nondiagnostic", l: "21%", su: "4.5%", h: "7.7%", avg: "~21–23%", rc: 2, anc: "CEA + amylase (cyst fluid); repeat FNA with ROSE", bg: "FFFFFF" },
{ cat: "II — Negative", l: "13%", su: "2.5%", h: "1.0%", avg: "~2.5–13%",rc: 1, anc: "None routine; IHC if repeat shows atypia", bg: LGREY },
{ cat: "III — Atypical", l: "74%", su: "25.3%", h: "28.0%", avg: "25–74%", rc: 3, anc: "FISH (polysomy/deletion); SMAD4 IHC loss; p53 mutation analysis; repeat FNA", bg: "FFFFFF" },
{ cat: "IV-a — Neo: Benign", l: "14%", su: "0%", h: "0%", avg: "~0–14%", rc: 1, anc: "CEA cyst fluid; MUC stains; consider elective resection", bg: LGREY },
{ cat: "IV-b — Neo: Other", l: "14%", su: "27.1%", h: "30.3%", avg: "~27–92%", rc: 4, anc: "Synaptophysin/Chromogranin A; Ki-67 index (NET grading); KRAS/GNAS mutation", bg: "FFFBE6" },
{ cat: "V — Suspicious", l: "82%", su: "75%", h: "100%", avg: "75–86%", rc: 4, anc: "Cell block IHC; CK7/CK20; CEA; CA19-9; p53 IHC", bg: "FFF0E6" },
{ cat: "VI — Malignant", l: "97%", su: "87.9%", h: "100%", avg: "87–100%", rc: 5, anc: "HER2 IHC + FISH (targeted Rx eligibility); NGS panel; TP53; EGFR/KRAS", bg: "FDECEA" },
];
const rowH = 0.68;
romRows.forEach((r, i) => {
const y = tblY + 0.38 + i * rowH;
s.addShape(pres.shapes.RECTANGLE, { x: 0.15, y, w: W - 0.3, h: rowH - 0.03, fill: { color: r.bg }, line: { color: MGREY, width: 0.4 } });
s.addText(r.cat, { x: tblCols[0].x + 0.05, y, w: tblCols[0].w - 0.10, h: rowH - 0.03, fontSize: 8.5, bold: true, color: NAVY, valign: "middle", fontFace: "Calibri" });
// Coloured ROM value cells
const romColors = [null, null, null, null]; // neutral for layfield/sung/hoda
const romBg2 = r.rc <= 1 ? GREEN : r.rc === 3 ? AMBER : r.rc === 4 ? ORANGE : RED;
[r.l, r.su, r.h].forEach((val, vi) => {
const col = tblCols[1 + vi];
s.addText(val, { x: col.x, y, w: col.w, h: rowH - 0.03, fontSize: 9, bold: true, color: romBg2, align: "center", valign: "middle", fontFace: "Calibri" });
});
// Avg ROM badge
const ac = tblCols[4];
s.addShape(pres.shapes.ROUNDED_RECTANGLE, { x: ac.x + 0.06, y: y + 0.15, w: ac.w - 0.12, h: rowH - 0.35, fill: { color: romBg2 }, line: { color: romBg2 }, rectRadius: 0.05 });
s.addText(r.avg, { x: ac.x + 0.06, y: y + 0.15, w: ac.w - 0.12, h: rowH - 0.35, fontSize: 8, bold: true, color: WHITE, align: "center", valign: "middle", fontFace: "Calibri" });
s.addText(r.anc, { x: tblCols[5].x + 0.06, y, w: tblCols[5].w - 0.10, h: rowH - 0.03, fontSize: 8, color: BLACK, valign: "middle", fontFace: "Calibri" });
// Vertical lines
[tblCols[1].x, tblCols[2].x, tblCols[3].x, tblCols[4].x, tblCols[5].x].forEach(lx => {
s.addShape(pres.shapes.RECTANGLE, { x: lx, y, w: 0.01, h: rowH - 0.03, fill: { color: MGREY }, line: { color: MGREY } });
});
});
// Source note
s.addText("Sources: Layfield et al. (Cancer Cytopathol 2020;128:29) · Sung et al. (Cancer Cytopathol 2020; PMID 31722125) · Hoda et al. · HER2 validation: Verma et al. J Clin Pathol 2024 (PMID 37221046)", {
x: 0.15, y: H - 0.60, w: W - 0.3, h: 0.26, fontSize: 7, color: "666666", fontFace: "Calibri", italic: true
});
footerBar(s, "4 / 6");
}
// ════════════════════════════════════════════════════════════════════════
// SLIDE 5 – Pitfalls & Clinical Pearls
// ════════════════════════════════════════════════════════════════════════
{
const s = pres.addSlide();
s.addShape(pres.shapes.RECTANGLE, { x: 0, y: 0, w: W, h: H, fill: { color: "F7FAFC" }, line: { color: "F7FAFC" } });
titleBar(s, "Pitfalls & Clinical Pearls — Gallbladder-Specific", "Critical points for accurate reporting and patient management");
const warnings = [
{ icon: "⚠", color: RED, title: "XGC is the #1 Mimic", body: "Xanthogranulomatous cholecystitis produces foamy histiocytes + reactive epithelial atypia mimicking Cat III–V. Key features favouring XGC over malignancy: monolayered reactive sheets, uniform chromatin, macrophage-predominant background, no coarse nuclear irregularity." },
{ icon: "⚠", color: RED, title: "Well-Diff. Adenocarcinoma Trap", body: "Well-differentiated GB adenocarcinoma may show minimal cytological atypia (reported as Cat II or III) because desmoplastic stroma and perineural invasion are architectural features NOT captured by FNA needle. Always correlate with imaging." },
{ icon: "⚠", color: ORANGE, title: "Negative NPV is Only ~71%", body: "A Category II (Negative) result does NOT exclude carcinoma in a radiologically suspicious gallbladder mass. Approximately 30% of GB carcinomas are false-negative on FNAC. Clinical + radiological suspicion overrides cytology alone." },
{ icon: "⚠", color: ORANGE, title: "Avoid FNA in Resectable Disease", body: "FNAC of a potentially resectable GB mass carries a risk of peritoneal seeding. Reserve FNAC for advanced/unresectable lesions where tissue confirmation is needed before palliative chemotherapy or targeted therapy." },
{ icon: "✔", color: GREEN, title: "Repeat Cat I or Cat III", body: "Inadequate (Cat I) or atypical (Cat III) results should prompt repeat FNA within 1 month when clinicoradiological suspicion is high. In one series, 76.9% of re-aspirated cases were positive for adenocarcinoma." },
{ icon: "✔", color: GREEN, title: "ROSE Reduces Inadequacy Rate", body: "Rapid On-Site Evaluation (ROSE) by a cytopathologist during EUS/US-guided FNA significantly reduces the Cat I nondiagnostic rate and guides needle re-passes to viable tumour areas rather than necrotic centre." },
{ icon: "✔", color: TEAL, title: "HER2 Testing on FNA Cell Block", body: "HER2/ERBB2 overexpression in >60% of GB carcinomas. IHC and FISH validated directly on FNA cell block material (Verma et al., J Clin Pathol 2024, PMID 37221046). Relevant for targeted therapy eligibility in advanced/unresectable disease." },
{ icon: "✔", color: TEAL, title: "WHO vs PSC — Practical Difference", body: "The main clinical upgrade of WHO 7-tier over PSC 6-tier is splitting Cat IV into PanN-Low (ROM ~0%, conservative) and PanN-High (ROM 27–92%, surgical urgency). Categories I–III and V–VI criteria remain identical between systems." },
];
const cardW2 = (W - 0.45) / 2;
const cardH2 = 1.30;
warnings.forEach((w, i) => {
const col = i % 2;
const row = Math.floor(i / 2);
const x = 0.15 + col * (cardW2 + 0.15);
const y = 1.30 + row * (cardH2 + 0.10);
s.addShape(pres.shapes.RECTANGLE, { x, y, w: cardW2, h: cardH2, fill: { color: "FFFFFF" }, line: { color: w.color, width: 1.5 }, shadow: { type: "outer", color: "000000", blur: 4, offset: 2, angle: 135, opacity: 0.08 } });
// Left accent bar
s.addShape(pres.shapes.RECTANGLE, { x, y, w: 0.15, h: cardH2, fill: { color: w.color }, line: { color: w.color } });
// Icon
s.addText(w.icon, { x: x + 0.18, y: y + 0.08, w: 0.4, h: 0.4, fontSize: 16, color: w.color, fontFace: "Calibri", valign: "middle" });
// Title
s.addText(w.title, { x: x + 0.55, y: y + 0.08, w: cardW2 - 0.65, h: 0.38, fontSize: 9.5, bold: true, color: w.color, fontFace: "Calibri", valign: "middle" });
// Body
s.addText(w.body, { x: x + 0.20, y: y + 0.48, w: cardW2 - 0.30, h: cardH2 - 0.55, fontSize: 8, color: BLACK, fontFace: "Calibri", valign: "top" });
});
footerBar(s, "5 / 6");
}
// ════════════════════════════════════════════════════════════════════════
// SLIDE 6 – WHO 7-Tier Expanded + Summary
// ════════════════════════════════════════════════════════════════════════
{
const s = pres.addSlide();
s.addShape(pres.shapes.RECTANGLE, { x: 0, y: 0, w: W, h: H, fill: { color: "F7FAFC" }, line: { color: "F7FAFC" } });
titleBar(s, "WHO 7-Tier System — What's New vs. PSC (Gallbladder Focus)", "Key changes, gallbladder-specific lesion mapping, and clinical impact");
const whoY = 1.30;
const whoCols = [
{ x: 0.15, w: 1.85, label: "WHO Category" },
{ x: 2.02, w: 3.20, label: "Key Change from PSC" },
{ x: 5.24, w: 3.80, label: "Gallbladder-Specific Lesions" },
{ x: 9.06, w: 4.05, label: "Clinical Impact" },
];
whoCols.forEach(c => {
s.addShape(pres.shapes.RECTANGLE, { x: c.x, y: whoY, w: c.w, h: 0.38, fill: { color: TEAL }, line: { color: TEAL } });
s.addText(c.label, { x: c.x, y: whoY, w: c.w, h: 0.38, fontSize: 9, bold: true, color: WHITE, align: "center", valign: "middle", fontFace: "Calibri" });
});
const whoRows = [
{ cat: "1. Insufficient/ND", bg: "FFFFFF", change: "Same as PSC Cat I — scant/uninterpretable material", lesions: "Same", impact: "Repeat FNA; no change from PSC" },
{ cat: "2. Benign/NFM", bg: LGREY, change: "Same as PSC Cat II — normal biliary epithelium", lesions: "Chronic cholecystitis; XGC; adenoma; GB tuberculosis", impact: "Conservative; elective cholecystectomy" },
{ cat: "3. Atypical", bg: "FFFFFF", change: "Same criteria as PSC Cat III; WHO acknowledges wider ROM range for bile duct (25–61%)", lesions: "XGC reactive atypia; reparative changes post-cholecystitis", impact: "MDT review; repeat FNA; molecular testing" },
{ cat: "4. PanN-Low\n★ NEW", bg: "EAF7EE", change: "Replaces PSC Cat IV-a (Benign); explicitly covers ALL low-grade intraductal/cystic neoplasms (ROM ~0–14%)", lesions: "ICPTN low-grade; BilIN low-grade; serous cystadenoma", impact: "Follow-up or elective cholecystectomy; no urgency" },
{ cat: "5. PanN-High\n★ NEW", bg: "FFFBE6", change: "SEPARATE TIER — high-grade precursors split from Cat IV-b; ROM 27–92%; triggers surgical urgency", lesions: "ICPTN high-grade; BilIN high-grade (biliary brushings); NET grade-dependent", impact: "Surgical referral to HPB centre; ancillary testing" },
{ cat: "6. Suspicious", bg: "FFF0E6", change: "Same as PSC Cat V — malignant features present but insufficient for definitive diagnosis", lesions: "Well-differentiated adenocarcinoma (classic trap)", impact: "Urgent HPB referral; treat as malignant" },
{ cat: "7. Malignant", bg: "FDECEA", change: "Same as PSC Cat VI — unequivocal malignant cytology", lesions: "Adenocarcinoma (87%); SCC; NEC; undifferentiated", impact: "Urgent HPB centre; staging; resectability" },
];
const rH = 0.68;
whoRows.forEach((r, i) => {
const y = whoY + 0.38 + i * rH;
s.addShape(pres.shapes.RECTANGLE, { x: 0.15, y, w: W - 0.3, h: rH - 0.03, fill: { color: r.bg }, line: { color: MGREY, width: 0.4 } });
const isCatNew = r.cat.includes("NEW");
s.addText(r.cat, { x: whoCols[0].x + 0.04, y, w: whoCols[0].w - 0.08, h: rH - 0.03, fontSize: 8.5, bold: true, color: isCatNew ? TEAL : NAVY, valign: "middle", fontFace: "Calibri" });
s.addText(r.change, { x: whoCols[1].x + 0.05, y, w: whoCols[1].w - 0.10, h: rH - 0.03, fontSize: 8, color: BLACK, valign: "middle", fontFace: "Calibri" });
s.addText(r.lesions, { x: whoCols[2].x + 0.05, y, w: whoCols[2].w - 0.10, h: rH - 0.03, fontSize: 8, color: BLACK, valign: "middle", fontFace: "Calibri" });
s.addText(r.impact, { x: whoCols[3].x + 0.05, y, w: whoCols[3].w - 0.10, h: rH - 0.03, fontSize: 8, color: BLACK, valign: "middle", fontFace: "Calibri" });
[whoCols[1].x, whoCols[2].x, whoCols[3].x].forEach(lx => {
s.addShape(pres.shapes.RECTANGLE, { x: lx, y, w: 0.01, h: rH - 0.03, fill: { color: MGREY }, line: { color: MGREY } });
});
});
// Take-home box
s.addShape(pres.shapes.RECTANGLE, { x: 0.15, y: H - 0.88, w: W - 0.30, h: 0.52, fill: { color: NAVY }, line: { color: NAVY }, shadow: { type: "outer", color: "000000", blur: 4, offset: 2, angle: 135, opacity: 0.12 } });
s.addText([
{ text: "Take-Home: ", options: { bold: true, color: "A8D4DF" } },
{ text: "PSC and WHO are equivalent for GB masses across Cat I–III and Cat V–VI. The sole major clinical difference is the WHO split of PSC Cat IV into PanN-Low (ROM ~0%, watch) vs PanN-High (ROM 27–92%, surgery). Both systems require clinical + radiological correlation — cytology alone has ~71% NPV for GB carcinoma.", options: { color: WHITE } }
], { x: 0.25, y: H - 0.88, w: W - 0.50, h: 0.52, fontSize: 8.5, fontFace: "Calibri", valign: "middle" });
footerBar(s, "6 / 6");
}
// ── Write file ────────────────────────────────────────────────────────────
pres.writeFile({ fileName: "/home/daytona/workspace/GB_Cytology_PSC_vs_WHO.pptx" })
.then(() => console.log("PPTX created: /home/daytona/workspace/GB_Cytology_PSC_vs_WHO.pptx"))
.catch(e => { console.error(e); process.exit(1); });
cd /home/daytona/workspace/gb-cytology-pdf && node build_pptx.js 2>&1
ls -lh /home/daytona/workspace/GB_Cytology_PSC_vs_WHO.pptx
GB Cytology PSC vs WHO
Presentation · PPTX
| Slide | Content |
|---|---|
| 1 | Title / overview with systems defined and slide roadmap |
| 2 | Full side-by-side comparison table — PSC vs WHO, colour-coded ROM badges (green → red), management, and GB-specific entities per category |
| 3 | 6 cytological criteria cards (one per reportable category) — architecture, nuclear features, pitfalls, ROM range |
| 4 | ROM data table with three studies (Layfield, Sung, Hoda) side by side + ancillary tests per category |
| 5 | 8 pitfall/pearl cards — warnings (red/orange) and best-practice tips (green/teal) |
| 6 | WHO 7-tier expanded table + take-home summary banner at the bottom |