essay answer on thornwald cyst
Thornwald cyst nasopharynx anatomy pathology clinical features
https://www.ejgm.co.uk/download/thornwaldts-cyst-7012.pdf
Tornwaldt cyst nasopharynx
Thornwald Tornwaldt cyst nasopharynx MRI endoscopy
| Symptom | Mechanism |
|---|---|
| Postnasal drip, halitosis, foul-tasting discharge | Intermittent drainage of cyst contents |
| Cervical pain and stiffness | Spasm of the longus capitis and prevertebral muscles adjacent to the cyst |
| Vertigo | Secondary to muscle spasm; not vestibular |
| Nasal obstruction | Large cysts causing mechanical obstruction |
| Otitis media with effusion (OME) / Eustachian tube dysfunction | Cyst proximity or expansion compressing the Eustachian tube orifice |
| Headache | Due to muscular tension |
| Snoring / sleep-disordered breathing | Particularly in giant cysts |


| Lesion | Key Distinguishing Feature |
|---|---|
| Rathke's pouch cyst | Midline, but lined by stratified squamous epithelium; extends toward sella |
| Adenoid retention cyst | Respiratory epithelium; lateral or diffuse adenoid tissue context |
| Branchial cleft cyst | Lateral position (not midline) |
| Sphenoid sinus mucocele | Originates in sphenoid sinus; bony expansion |
| Nasopharyngeal encephalocele/meningocoele | Intracranial connection on MRI; must be excluded BEFORE surgery |
| Nasopharyngeal carcinoma | Mass effect, mucosa destruction, lymphadenopathy, contrast enhancement |
| Neurenteric cyst | Posterior fossa or spinal location; intracranial communication |
| Juvenile angiofibroma | Males, arising from sphenopalatine foramen; highly vascular on imaging |
make pdf
ls /home/daytona/workspace/ 2>/dev/null || echo "workspace empty or missing"
mkdir -p /home/daytona/workspace/thornwald-cyst-essay
~/thornwald-cyst-essay/generate_pdf.py
from reportlab.lib.pagesizes import A4
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.units import cm
from reportlab.lib import colors
from reportlab.platypus import (
SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
HRFlowable, Image as RLImage
)
from reportlab.lib.enums import TA_LEFT, TA_CENTER, TA_JUSTIFY
import urllib.request
import os
import io
OUTPUT = "/home/daytona/workspace/thornwald-cyst-essay/Thornwald_Cyst_Essay.pdf"
# Download images
img_urls = {
"mri_t2.jpg": "https://cdn.orris.care/cdss_images/pmc_clinical_VQA_a63d53a4e15973faba561bf34d7e192a8302bcb1043c3b0cf269848287b434b6.jpg",
"ct_axial.jpg": "https://cdn.orris.care/cdss_images/pmc_clinical_VQA_130c4b2be0867c4d805d1a7d365bd58fdd49bd204e0904f90bb68b02ee4775e3.jpg",
}
img_dir = "/home/daytona/workspace/thornwald-cyst-essay/"
for fname, url in img_urls.items():
path = os.path.join(img_dir, fname)
if not os.path.exists(path):
try:
urllib.request.urlretrieve(url, path)
print(f"Downloaded {fname}")
except Exception as e:
print(f"Could not download {fname}: {e}")
# Styles
doc = SimpleDocTemplate(
OUTPUT,
pagesize=A4,
rightMargin=2*cm, leftMargin=2*cm,
topMargin=2.5*cm, bottomMargin=2.5*cm,
title="Thornwald Cyst — A Comprehensive Essay",
author="Orris Medical AI"
)
styles = getSampleStyleSheet()
title_style = ParagraphStyle(
'Title', parent=styles['Title'],
fontSize=22, textColor=colors.HexColor('#1a3a5c'),
spaceAfter=6, alignment=TA_CENTER, fontName='Helvetica-Bold'
)
subtitle_style = ParagraphStyle(
'Subtitle', parent=styles['Normal'],
fontSize=11, textColor=colors.HexColor('#4a6fa5'),
spaceAfter=12, alignment=TA_CENTER, fontName='Helvetica-Oblique'
)
h1_style = ParagraphStyle(
'H1', parent=styles['Heading1'],
fontSize=14, textColor=colors.HexColor('#1a3a5c'),
spaceBefore=16, spaceAfter=6, fontName='Helvetica-Bold',
borderPad=4
)
h2_style = ParagraphStyle(
'H2', parent=styles['Heading2'],
fontSize=11, textColor=colors.HexColor('#2c5f8a'),
spaceBefore=10, spaceAfter=4, fontName='Helvetica-Bold'
)
body_style = ParagraphStyle(
'Body', parent=styles['Normal'],
fontSize=10, leading=15, alignment=TA_JUSTIFY,
spaceAfter=6, fontName='Helvetica'
)
bullet_style = ParagraphStyle(
'Bullet', parent=styles['Normal'],
fontSize=10, leading=14, leftIndent=16,
bulletIndent=6, spaceAfter=3, fontName='Helvetica'
)
caption_style = ParagraphStyle(
'Caption', parent=styles['Normal'],
fontSize=8.5, textColor=colors.HexColor('#555555'),
alignment=TA_CENTER, spaceAfter=8, fontName='Helvetica-Oblique'
)
ref_style = ParagraphStyle(
'Ref', parent=styles['Normal'],
fontSize=8.5, leading=12, textColor=colors.HexColor('#444444'),
spaceAfter=3, fontName='Helvetica'
)
def h1(text):
return [HRFlowable(width="100%", thickness=1.5, color=colors.HexColor('#1a3a5c'), spaceAfter=4),
Paragraph(text, h1_style)]
def h2(text):
return [Paragraph(text, h2_style)]
def body(text):
return [Paragraph(text, body_style)]
def bullet(items):
return [Paragraph(f"• {item}", bullet_style) for item in items]
story = []
# Title block
story.append(Spacer(1, 0.3*cm))
story.append(Paragraph("Thornwald (Tornwaldt) Cyst", title_style))
story.append(Paragraph("A Comprehensive Essay — ENT / Head & Neck", subtitle_style))
story.append(HRFlowable(width="100%", thickness=2, color=colors.HexColor('#4a6fa5'), spaceAfter=14))
# 1. Definition
story += h1("1. Definition and Nomenclature")
story += body(
"A <b>Thornwald cyst</b> (also spelled Tornwaldt's cyst, or Tornwaldt's bursa) is a benign, midline cystic lesion "
"located in the roof of the nasopharynx, arising from the persistent remnant of the embryonic pharyngeal bursa. "
"The eponym derives from German physician <b>Gustav Ludwig Tornwaldt</b>, who in 1885 published a detailed account "
"of 26 nasopharyngeal cases, describing both clinical symptoms and treatment. The original description is attributed "
"to <b>A.F.C.J. Meyer</b> in 1840. When the cyst becomes symptomatic, the condition is termed <b>Tornwaldt's disease</b>."
)
# 2. Embryology
story += h1("2. Embryology and Pathogenesis")
story += body(
"The <b>pharyngeal bursa</b> (Bursa of Luschka) forms during fetal development as a result of the transient adhesion "
"between the notochord (mesenchymal origin) and the roof of the primitive nasopharynx (endodermal origin). This "
"communication normally obliterates before birth. When it persists and the ostium becomes obstructed, "
"trapped secretions accumulate within the blind-ended pouch, resulting in a Thornwald cyst."
)
story += body("Two categories are recognised based on the drainage pathway:")
story += bullet([
"<b>Cystic type</b> — Obstructed drainage, leading to progressive cyst formation and fluid accumulation.",
"<b>Crusting type</b> — Spontaneous drainage into the nasopharynx, resulting in crusting and postnasal discharge.",
])
story += body(
"Approximately <b>75% of cases</b> are attributed to trauma to the nasopharynx — including nasopharyngitis, "
"adenoidectomy, or chemoradiation for nasopharyngeal carcinoma — which obstructs the bursal orifice."
)
# 3. Epidemiology
story += h1("3. Epidemiology")
story += body(
"Based on MRI studies, the reported incidence of Thornwald cysts ranges from <b>1.5% to 5.3%</b> of the general "
"population, making them one of the more common incidental nasopharyngeal findings. They are most frequently "
"identified in individuals aged <b>15 to 60 years</b>, with gradual fluid accumulation explaining the later age "
"of detection. There is no strong sex predilection."
)
# 4. Anatomy
story += h1("4. Anatomy and Location")
story += body(
"The pharyngeal bursa is situated above the superior pharyngeal constrictor muscle, approximately at the level "
"of the <b>fossa of Rosenmuller</b>, nestled between the <b>longus capitis muscles</b> and extending superiorly "
"toward the <b>nasopharyngeal tubercle of the occiput</b>. Thornwald cysts are therefore characteristically "
"located in the <b>midline of the posterior nasopharyngeal roof</b>, deep to intact mucosa. Rarely, they may be "
"slightly off-midline."
)
# 5. Pathology
story += h1("5. Pathology")
story += h2("Macroscopic")
story += body(
"A smooth, well-encapsulated submucosal cystic mass. Size ranges from a few millimeters to several centimeters; "
"most typical cysts measure <b>2–10 mm</b> in diameter. Giant cysts exceeding 4 cm have been reported and can "
"cause significant airway compromise."
)
story += h2("Microscopic")
story += body(
"The cyst wall is lined by <b>pseudostratified ciliated columnar (respiratory) epithelium</b>. Cyst contents are "
"mucoid to mucopurulent, with variable degrees of lymphocytic infiltration when infected. This histological "
"appearance is a key distinguishing feature:"
)
# Histology comparison table
hist_data = [
['Lesion', 'Epithelial Lining', 'Location'],
['Thornwald cyst', 'Pseudostratified ciliated columnar', 'Midline posterior nasopharynx'],
["Rathke's cleft cyst", 'Stratified squamous', 'Midline, extends toward sella'],
['Adenoid retention cyst', 'Respiratory epithelium', 'Lateral / diffuse adenoid tissue'],
['Branchial cleft cyst', 'Stratified squamous ± columnar', 'Lateral neck/nasopharynx'],
]
hist_table = Table(hist_data, colWidths=[5.2*cm, 6.2*cm, 5.6*cm])
hist_table.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,0), colors.HexColor('#1a3a5c')),
('TEXTCOLOR', (0,0), (-1,0), colors.white),
('FONTNAME', (0,0), (-1,0), 'Helvetica-Bold'),
('FONTSIZE', (0,0), (-1,-1), 9),
('BACKGROUND', (0,1), (-1,-1), colors.HexColor('#f0f4f8')),
('ROWBACKGROUNDS', (0,1), (-1,-1), [colors.HexColor('#f0f4f8'), colors.white]),
('GRID', (0,0), (-1,-1), 0.5, colors.HexColor('#bbccdd')),
('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
('LEFTPADDING', (0,0), (-1,-1), 6),
('RIGHTPADDING', (0,0), (-1,-1), 6),
('TOPPADDING', (0,0), (-1,-1), 4),
('BOTTOMPADDING', (0,0), (-1,-1), 4),
]))
story.append(Spacer(1, 4))
story.append(hist_table)
story.append(Spacer(1, 8))
# 6. Clinical Features
story += h1("6. Clinical Features")
story += body(
"The majority of Thornwald cysts are <b>asymptomatic incidental findings</b> on imaging. When symptomatic "
"(Tornwaldt's disease), the following features are recognised:"
)
clin_data = [
['Symptom / Sign', 'Mechanism'],
['Postnasal drip, halitosis, foul-tasting discharge', 'Intermittent drainage of cyst contents'],
['Cervical pain and stiffness', 'Spasm of longus capitis / prevertebral muscles'],
['Vertigo', 'Secondary to muscular spasm (not vestibular)'],
['Nasal obstruction', 'Mechanical — large cysts'],
['Otitis media with effusion (OME)', 'Eustachian tube compression'],
['Headache', 'Muscular tension; prevertebral involvement'],
['Snoring / sleep-disordered breathing', 'Airway narrowing by giant cysts'],
]
clin_table = Table(clin_data, colWidths=[9*cm, 8*cm])
clin_table.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,0), colors.HexColor('#2c5f8a')),
('TEXTCOLOR', (0,0), (-1,0), colors.white),
('FONTNAME', (0,0), (-1,0), 'Helvetica-Bold'),
('FONTSIZE', (0,0), (-1,-1), 9),
('ROWBACKGROUNDS', (0,1), (-1,-1), [colors.HexColor('#eef2f7'), colors.white]),
('GRID', (0,0), (-1,-1), 0.5, colors.HexColor('#bbccdd')),
('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
('LEFTPADDING', (0,0), (-1,-1), 6),
('RIGHTPADDING', (0,0), (-1,-1), 6),
('TOPPADDING', (0,0), (-1,-1), 4),
('BOTTOMPADDING', (0,0), (-1,-1), 4),
]))
story.append(clin_table)
story.append(Spacer(1, 6))
story += body(
"The classical <b>Tornwaldt triad</b> — cervical pain/stiffness, vertigo, and postnasal drip — is considered "
"pathognomonic of symptomatic disease. Notably, frank nasal symptoms are often not the dominant complaint."
)
# 7. Diagnosis
story += h1("7. Diagnosis")
story += h2("Clinical / Endoscopic")
story += body(
"<b>Diagnostic nasal endoscopy</b> reveals a smooth, submucosal, well-encapsulated mass along the posterior "
"roof of the nasopharynx, typically midline and covered by intact mucosa. The pharyngeal orifice of the bursa "
"may not be visible even when patent."
)
story += h2("Imaging")
story += body(
"<b>CT scan</b> demonstrates a well-defined, sharply marginated, hypodense soft tissue mass on the posterior "
"nasopharyngeal wall without bony erosion or surrounding soft tissue reaction."
)
story += body(
"<b>MRI is the gold standard</b> for characterisation and is preferred over CT for accurate evaluation:"
)
story += bullet([
"<b>T1-weighted:</b> Intermediate to high signal intensity (variable depending on protein content)",
"<b>T2-weighted:</b> Characteristically <b>hyperintense</b> (bright) — the most consistent MRI feature",
"<b>Post-gadolinium:</b> No enhancement — critical distinction from solid or necrotic neoplasm",
"Well-circumscribed, thin-walled lesion between the longus capitis muscles at the midline posterior nasopharynx",
"Size typically 2–10 mm; no adjacent bone erosion or soft tissue infiltration",
])
# Images
story.append(Spacer(1, 8))
mri_path = os.path.join(img_dir, "mri_t2.jpg")
ct_path = os.path.join(img_dir, "ct_axial.jpg")
img_row = []
if os.path.exists(mri_path):
img_row.append(RLImage(mri_path, width=7.8*cm, height=6*cm))
else:
img_row.append(Paragraph("[MRI image unavailable]", caption_style))
if os.path.exists(ct_path):
img_row.append(RLImage(ct_path, width=7.8*cm, height=6*cm))
else:
img_row.append(Paragraph("[CT image unavailable]", caption_style))
img_table = Table([img_row], colWidths=[8.5*cm, 8.5*cm])
img_table.setStyle(TableStyle([
('ALIGN', (0,0), (-1,-1), 'CENTER'),
('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
('LEFTPADDING', (0,0), (-1,-1), 4),
('RIGHTPADDING', (0,0), (-1,-1), 4),
]))
story.append(img_table)
cap_row = [
Paragraph("<i>Fig 1. Axial T2-weighted MRI — hyperintense midline Tornwaldt cyst (arrow) between the longus capitis muscles.</i>", caption_style),
Paragraph("<i>Fig 2. Axial CT — large hypodense midline Thornwald cyst causing airway compromise (4.6 × 2.7 cm).</i>", caption_style),
]
cap_table = Table([cap_row], colWidths=[8.5*cm, 8.5*cm])
cap_table.setStyle(TableStyle([
('ALIGN', (0,0), (-1,-1), 'CENTER'),
('LEFTPADDING', (0,0), (-1,-1), 4),
('RIGHTPADDING', (0,0), (-1,-1), 4),
]))
story.append(cap_table)
story.append(Spacer(1, 6))
# 8. Differential Diagnosis
story += h1("8. Differential Diagnosis")
diff_data = [
['Lesion', 'Key Distinguishing Feature'],
["Rathke's pouch cyst", "Midline; stratified squamous lining; extends toward sella turcica"],
["Adenoid retention cyst", "Respiratory epithelium; lateral/diffuse; no embryological bursa"],
["Branchial cleft cyst", "Lateral position — not midline"],
["Sphenoid sinus mucocele", "Originates in sphenoid sinus; bony expansion on CT"],
["Nasopharyngeal encephalocele", "Intracranial connection on MRI — MUST exclude before surgery"],
["Nasopharyngeal carcinoma", "Mucosal destruction, enhancement, cervical lymphadenopathy"],
["Neurenteric cyst", "Posterior fossa / spinal; may have intracranial communication"],
["Juvenile angiofibroma", "Young males; sphenopalatine foramen origin; highly vascular"],
]
diff_table = Table(diff_data, colWidths=[6*cm, 11*cm])
diff_table.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,0), colors.HexColor('#1a3a5c')),
('TEXTCOLOR', (0,0), (-1,0), colors.white),
('FONTNAME', (0,0), (-1,0), 'Helvetica-Bold'),
('FONTSIZE', (0,0), (-1,-1), 9),
('ROWBACKGROUNDS', (0,1), (-1,-1), [colors.HexColor('#f0f4f8'), colors.white]),
('GRID', (0,0), (-1,-1), 0.5, colors.HexColor('#bbccdd')),
('VALIGN', (0,0), (-1,-1), 'TOP'),
('LEFTPADDING', (0,0), (-1,-1), 6),
('RIGHTPADDING', (0,0), (-1,-1), 6),
('TOPPADDING', (0,0), (-1,-1), 4),
('BOTTOMPADDING', (0,0), (-1,-1), 4),
]))
story.append(diff_table)
story.append(Spacer(1, 6))
story += body(
"<b>Critical point:</b> A nasopharyngeal encephalocele must be excluded before any surgical intervention, "
"as inadvertent incision could result in a catastrophic CSF leak or meningitis."
)
# 9. Treatment
story += h1("9. Treatment")
story += body(
"<b>Asymptomatic cysts</b> require no intervention — clinical and imaging surveillance is appropriate."
)
story += body("<b>Symptomatic cysts</b> are managed surgically:")
story += bullet([
"<b>Transnasal endoscopic excision</b> — complete removal under endoscopic guidance; the definitive approach.",
"<b>Marsupialization</b> — wide opening of the cyst roof via transnasal approach; prevents re-accumulation and lowers recurrence risk.",
"Both approaches result in <b>complete symptom resolution</b> in the vast majority of patients.",
"Aspiration alone is not recommended due to high recurrence.",
"Antibiotics may be used for acute superinfection prior to surgery but are not curative.",
])
# 10. Complications
story += h1("10. Complications")
story += bullet([
"<b>Superinfection</b> (Thornwaldt's disease flare) — purulent postnasal drip, halitosis, febrile illness; most common complication.",
"<b>Otitis media with effusion</b> — due to Eustachian tube compression by large cysts.",
"<b>Airway obstruction</b> — rare; seen with giant cysts, particularly in neonates.",
"<b>Recurrence</b> — possible after incomplete excision; marsupialization reduces this risk.",
])
# 11. Key Points
story += h1("11. Key Points Summary")
kp_data = [
['Key Point', 'Detail'],
['Nature', 'Benign midline developmental cyst of the nasopharynx'],
['Origin', 'Persistent embryonic pharyngeal bursa (notochord ↔ nasopharynx adhesion)'],
['Incidence', '1.5–5.3% of population (common incidental MRI finding)'],
['Location', 'Midline posterior nasopharyngeal roof, between longus capitis muscles'],
['Histology', 'Pseudostratified ciliated columnar (respiratory) epithelium'],
['Classic symptoms', 'Cervical pain/stiffness, vertigo, postnasal drip (Tornwaldt triad)'],
['Gold standard imaging', 'MRI: T2 hyperintense, T1 variable, no gadolinium enhancement'],
['Critical DDx', 'Nasopharyngeal encephalocele — exclude before surgery'],
['Treatment', 'Asymptomatic: observe; Symptomatic: endoscopic excision / marsupialization'],
]
kp_table = Table(kp_data, colWidths=[5.5*cm, 11.5*cm])
kp_table.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,0), colors.HexColor('#1a3a5c')),
('TEXTCOLOR', (0,0), (-1,0), colors.white),
('FONTNAME', (0,0), (-1,0), 'Helvetica-Bold'),
('FONTSIZE', (0,0), (-1,-1), 9),
('FONTNAME', (0,1), (0,-1), 'Helvetica-Bold'),
('TEXTCOLOR', (0,1), (0,-1), colors.HexColor('#1a3a5c')),
('ROWBACKGROUNDS', (0,1), (-1,-1), [colors.HexColor('#eef7f0'), colors.white]),
('GRID', (0,0), (-1,-1), 0.5, colors.HexColor('#bbccdd')),
('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
('LEFTPADDING', (0,0), (-1,-1), 6),
('RIGHTPADDING', (0,0), (-1,-1), 6),
('TOPPADDING', (0,0), (-1,-1), 5),
('BOTTOMPADDING', (0,0), (-1,-1), 5),
]))
story.append(kp_table)
story.append(Spacer(1, 12))
# References
story += h1("References")
refs = [
"1. Tornwaldt GL. Zur Frage der Bursa Pharyngea. Deut Med Wochenschr. 1887.",
"2. Miyahara H, Matsunaga T. Tornwaldt's disease. Acta Otolaryngol Suppl. 1994. [PMID 7856446]",
"3. Miller RH, Sneed WF. Thornwaldt's bursa. Clin Otolaryngol. 1985;10:21–5. [PMID 4006264]",
"4. Huang CY, Kang BH. Tornwaldt's Cyst. N Engl J Med. 2019;380(20):1961. [PMID 31091377]",
"5. Lee JH. Huge Tornwaldt Cyst With Otitis Media With Effusion. Ear Nose Throat J. 2021. [PMID 31760788]",
"6. Konsulov S, et al. Symptomatic Tornwaldt Cyst: A Case Report. Cureus. 2024. [PMID 38784350]",
"7. EJGM — Thornwaldt's Cyst: Electronic Journal of General Medicine. Konya University.",
"8. Ikushima I, et al. MR imaging of Thornwaldt's cysts. AJR Am J Roentgenol. 1999;172:1663–5.",
]
for r in refs:
story.append(Paragraph(r, ref_style))
story.append(Spacer(1, 8))
story.append(HRFlowable(width="100%", thickness=0.5, color=colors.HexColor('#aaaaaa')))
story.append(Spacer(1, 4))
story.append(Paragraph("Generated by Orris Medical AI — For educational purposes only.", caption_style))
doc.build(story)
print(f"PDF saved to {OUTPUT}")
python /home/daytona/workspace/thornwald-cyst-essay/generate_pdf.py
Thornwald Cyst Essay
PDF Document · PDF