Give essay answer on atelectasis of middle ear , grades presentation investigations and management
atelectasis middle ear tympanic membrane retraction

| Stage | Description |
|---|---|
| Stage I | Retracted tympanic membrane — mild inward bowing, but membrane still mobile and not in contact with ossicles |
| Stage II | Severe retraction — tympanic membrane in contact with the incus (long process) but middle ear space not fully obliterated |
| Stage III | True middle ear atelectasis — tympanic membrane lies on the promontory; middle ear space partially or completely obliterated; membrane still not adherent; mucosa intact |
| Stage IV | Adhesive otitis media — total obliteration of middle ear space; tympanic membrane adherent to ossicles and promontory; mucosal surfaces lost on both medial side of TM and middle ear lining |
| Finding | Stage |
|---|---|
| Slightly dull, indrawn pars tensa | Stage I |
| TM touching long process of incus; incus may appear prominently through the membrane | Stage II |
| TM draped over promontory; "disappearing" middle ear space; TM moves toward the medial wall on Valsalva, not away | Stage III |
| Flat, featureless TM plastered to ossicles and promontory; no middle ear space | Stage IV (adhesive OM) |

| Complication | Mechanism |
|---|---|
| Adhesive otitis media | End-stage atelectasis (Stage IV) |
| Conductive hearing loss | Obliteration of middle ear space + ossicular erosion |
| Sensorineural hearing loss | Chronic middle ear inflammation extending to inner ear |
| Primary acquired cholesteatoma | Keratin accumulation in non-self-cleaning retraction pocket |
| Ossicular erosion | Pressure necrosis from retracted TM; incus long process most vulnerable |
| Tympanosclerosis | Collagen destruction and calcification within TM |
| Labyrinthine fistula | From cholesteatoma erosion of lateral semicircular canal |
| Facial nerve paralysis | Cholesteatoma extending to fallopian canal |
| Intracranial infection | Advanced cholesteatoma |
Make it into pdf please
ls -la /home/daytona/workspace/ 2>/dev/null || echo "workspace does not exist"
mkdir -p /home/daytona/workspace/atelectasis-essay
~/atelectasis-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, PageBreak
)
from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_JUSTIFY
from reportlab.platypus import KeepTogether
import urllib.request
import os
OUTPUT_PATH = "/home/daytona/workspace/atelectasis-essay/Atelectasis_Middle_Ear.pdf"
# Download the two images
img_urls = {
"stages.png": "https://cdn.orris.care/cdss_images/acfa234321bbb8ab87fcc98c2fbf043f5f1c38688b297306c5803a252de71060.png",
"otoscopy.png": "https://cdn.orris.care/cdss_images/6a3f4c16fff74dfcc030e2998b3806a4af84d217ff0284acfb12cb7533245e71.png",
}
img_dir = "/home/daytona/workspace/atelectasis-essay/"
for fname, url in img_urls.items():
path = os.path.join(img_dir, fname)
if not os.path.exists(path):
urllib.request.urlretrieve(url, path)
print(f"Downloaded {fname}")
# ── Document setup ──────────────────────────────────────────────────────────
doc = SimpleDocTemplate(
OUTPUT_PATH,
pagesize=A4,
leftMargin=2.5*cm, rightMargin=2.5*cm,
topMargin=2.5*cm, bottomMargin=2.5*cm,
title="Atelectasis of the Middle Ear",
author="Orris Medical AI",
)
W = A4[0] - 5*cm # usable width
# ── Styles ───────────────────────────────────────────────────────────────────
styles = getSampleStyleSheet()
NAVY = colors.HexColor("#1a3a5c")
TEAL = colors.HexColor("#0d7377")
LIGHT = colors.HexColor("#e8f4f8")
BORDER = colors.HexColor("#2c7bb6")
WHITE = colors.white
GREY = colors.HexColor("#4a4a4a")
title_style = ParagraphStyle(
"DocTitle", fontSize=22, fontName="Helvetica-Bold",
textColor=WHITE, alignment=TA_CENTER, spaceAfter=4,
leading=26,
)
subtitle_style = ParagraphStyle(
"DocSubtitle", fontSize=11, fontName="Helvetica",
textColor=colors.HexColor("#d0e8f0"), alignment=TA_CENTER,
)
h1 = ParagraphStyle(
"H1", fontSize=14, fontName="Helvetica-Bold",
textColor=WHITE, spaceBefore=14, spaceAfter=4,
leading=18, leftIndent=0,
)
h2 = ParagraphStyle(
"H2", fontSize=12, fontName="Helvetica-Bold",
textColor=NAVY, spaceBefore=10, spaceAfter=4,
leading=16, leftIndent=0,
)
body = ParagraphStyle(
"Body", fontSize=10, fontName="Helvetica",
textColor=GREY, leading=15, spaceAfter=6,
alignment=TA_JUSTIFY,
)
bullet = ParagraphStyle(
"Bullet", fontSize=10, fontName="Helvetica",
textColor=GREY, leading=14, spaceAfter=3,
leftIndent=16, bulletIndent=6,
)
caption = ParagraphStyle(
"Caption", fontSize=8.5, fontName="Helvetica-Oblique",
textColor=colors.HexColor("#555555"), alignment=TA_CENTER,
spaceAfter=8, spaceBefore=4,
)
source_style = ParagraphStyle(
"Source", fontSize=8, fontName="Helvetica-Oblique",
textColor=colors.HexColor("#888888"), alignment=TA_CENTER,
spaceBefore=20,
)
def section_header(text):
"""Navy banner with white text for major sections."""
data = [[Paragraph(text, h1)]]
t = Table(data, colWidths=[W])
t.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), NAVY),
("TOPPADDING", (0,0), (-1,-1), 6),
("BOTTOMPADDING", (0,0), (-1,-1), 6),
("LEFTPADDING", (0,0), (-1,-1), 10),
("RIGHTPADDING", (0,0), (-1,-1), 10),
("ROWBACKGROUNDS",(0,0), (-1,-1), [NAVY]),
]))
return t
def sub_header(text):
return Paragraph(text, h2)
def body_para(text):
return Paragraph(text, body)
def bullet_para(text):
return Paragraph(f"• {text}", bullet)
def make_table(headers, rows, col_widths=None):
data = [[Paragraph(h, ParagraphStyle("TH", fontSize=9, fontName="Helvetica-Bold",
textColor=WHITE, leading=13)) for h in headers]]
for row in rows:
data.append([Paragraph(str(c), ParagraphStyle("TD", fontSize=9,
fontName="Helvetica", textColor=GREY, leading=13)) for c in row])
if col_widths is None:
col_widths = [W / len(headers)] * len(headers)
t = Table(data, colWidths=col_widths, repeatRows=1)
t.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,0), NAVY),
("ROWBACKGROUNDS",(0,1), (-1,-1), [WHITE, LIGHT]),
("GRID", (0,0), (-1,-1), 0.5, colors.HexColor("#b0c4de")),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING", (0,0), (-1,-1), 5),
("LEFTPADDING", (0,0), (-1,-1), 7),
("RIGHTPADDING", (0,0), (-1,-1), 7),
("VALIGN", (0,0), (-1,-1), "TOP"),
]))
return t
# ── Title Page Block ──────────────────────────────────────────────────────────
def title_block():
data = [[
Paragraph("ATELECTASIS OF THE MIDDLE EAR", title_style),
],[
Paragraph("A Comprehensive Medical Essay", subtitle_style),
],[
Paragraph("Grades · Presentation · Investigations · Management", subtitle_style),
]]
t = Table(data, colWidths=[W])
t.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), NAVY),
("TOPPADDING", (0,0), (0,0), 22),
("BOTTOMPADDING", (0,2), (0,2), 22),
("LEFTPADDING", (0,0), (-1,-1), 12),
("RIGHTPADDING", (0,0), (-1,-1), 12),
("TOPPADDING", (0,1), (0,1), 4),
("BOTTOMPADDING", (0,1), (0,1), 4),
]))
return t
# ═══════════════════════════════════════════════════════════════════════════
# BUILD STORY
# ═══════════════════════════════════════════════════════════════════════════
story = []
# Title
story.append(title_block())
story.append(Spacer(1, 0.6*cm))
story.append(HRFlowable(width=W, thickness=2, color=TEAL))
story.append(Spacer(1, 0.4*cm))
# ── 1. DEFINITION ─────────────────────────────────────────────────────────
story.append(section_header("1. DEFINITION"))
story.append(Spacer(1, 0.3*cm))
story.append(body_para(
"Middle ear atelectasis is a condition in which the tympanic membrane becomes progressively retracted into "
"the middle ear cavity due to longstanding negative middle ear pressure arising from eustachian tube "
"dysfunction. The middle ear space is partially or completely obliterated, but the tympanic membrane remains "
"<b>non-adherent</b> to the medial wall and the mucosal lining of the middle ear is preserved. "
"This distinguishes it from <b>adhesive otitis media</b> (Stage IV), where the tympanic membrane is fully "
"adherent to the ossicles and promontory with irreversible loss of the mucosal surfaces."
))
# ── 2. PATHOPHYSIOLOGY ────────────────────────────────────────────────────
story.append(Spacer(1, 0.2*cm))
story.append(section_header("2. PATHOPHYSIOLOGY"))
story.append(Spacer(1, 0.3*cm))
story.append(body_para(
"The principal driving mechanism is <b>eustachian tube dysfunction (ETD)</b>. The eustachian tube serves "
"three functions: ventilation of the middle ear, mucociliary drainage, and protection from nasopharyngeal "
"secretions. Intermittent opening of the tube allows gas exchange between the nasopharynx and middle ear, "
"equalising atmospheric pressure. Notably, the gas composition of the middle ear resembles that of venous "
"blood; bilateral diffusion between the middle ear cavity and blood further contributes to relative negative "
"middle ear pressure when ventilation is inadequate."
))
story.append(body_para("When eustachian tube function fails, the following sequence occurs:"))
for b in [
"Negative middle ear pressure develops progressively",
"The tympanic membrane is drawn medially by the pressure differential",
"Repeated bouts of acute otitis media (AOM) cause collagen destruction and thinning of the fibrous layer "
"of the tympanic membrane (Sadé and Berco), reducing structural resistance to retraction",
"The tympanic membrane progressively drapes over the ossicles and onto the promontory",
"If the process continues, mucosal surfaces are destroyed, resulting in adhesive otitis media",
]:
story.append(bullet_para(b))
story.append(Spacer(1, 0.2*cm))
story.append(sub_header("Predisposing Factors"))
for b in [
"Recurrent acute otitis media (AOM) and chronic otitis media with effusion (OME)",
"Cleft palate, Down syndrome, and craniofacial anomalies",
"Adenoidal hypertrophy and nasopharyngeal tumours",
"Post-irradiation sequelae (skull base / head and neck cancer)",
"Allergic rhinitis, chronic rhinosinusitis, and paranasal sinus disease",
"Gastro-oesophageal reflux disease — H. pylori detected in middle ear effusions",
"Hereditary predisposition: mastoid hypopneumatisation reduces gas reservoir, worsening pressure dynamics",
]:
story.append(bullet_para(b))
# ── 3. GRADING / CLASSIFICATION ───────────────────────────────────────────
story.append(Spacer(1, 0.2*cm))
story.append(section_header("3. GRADING / CLASSIFICATION"))
story.append(Spacer(1, 0.3*cm))
story.append(body_para(
"The most widely used system is the <b>Sadé and Berco classification (1976)</b>, later refined by Sadé and "
"Tos, staging retraction of the tympanic membrane on a spectrum from mild retraction to full adhesive otitis media:"
))
story.append(Spacer(1, 0.25*cm))
# Staging table
story.append(make_table(
["Stage", "Description"],
[
["Stage I — Retraction",
"Mild inward bowing of the tympanic membrane. Membrane is still mobile and not in contact with ossicles. "
"Middle ear space preserved."],
["Stage II — Severe Retraction",
"TM in contact with the long process of the incus. Middle ear space not fully obliterated. "
"Incus may be visible through the thinned membrane."],
["Stage III — True Atelectasis",
"TM lies on the promontory; middle ear space partially or completely obliterated. "
"TM is not adherent; mucosa intact. This is the true atelectatic stage."],
["Stage IV — Adhesive Otitis Media",
"Total obliteration of middle ear space. TM adherent to ossicles and promontory. "
"Mucosal surfaces on both medial TM and middle ear lining are irreversibly lost."],
],
col_widths=[4.5*cm, W - 4.5*cm],
))
story.append(Spacer(1, 0.3*cm))
# Stages diagram image
stages_img_path = img_dir + "stages.png"
if os.path.exists(stages_img_path):
img = Image(stages_img_path, width=W, height=W*0.28)
story.append(img)
story.append(Paragraph(
"Fig. 1 — The four stages of middle ear atelectasis (Normal → Stage I Retraction → Stage II Severe Retraction "
"→ Stage III Atelectasis → Stage IV Adhesive Otitis). Adapted from Sadé J, Berco E (1976); "
"reproduced from Cummings Otolaryngology Head and Neck Surgery.",
caption,
))
story.append(Spacer(1, 0.2*cm))
story.append(sub_header("Other Classification Systems"))
for b in [
"<b>Deguine classification</b> — specifically for pars tensa retraction pockets",
"<b>Tos classification</b> — for pars flaccida retractions (more relevant to attic cholesteatoma precursor pockets)",
]:
story.append(bullet_para(b))
# ── 4. CLINICAL PRESENTATION ──────────────────────────────────────────────
story.append(Spacer(1, 0.2*cm))
story.append(section_header("4. CLINICAL PRESENTATION"))
story.append(Spacer(1, 0.3*cm))
story.append(sub_header("Symptoms"))
story.append(make_table(
["Symptom", "Notes"],
[
["Hearing loss",
"Typically conductive, slowly progressive. Air-bone gap on audiogram. Worsens with ossicular erosion."],
["Aural fullness",
"Sensation of blocked or pressurised ear; common in all stages."],
["Autophony",
"Hearing one's own voice excessively; reflects altered TM compliance."],
["Recurrent otorrhoea",
"When co-existing OME or secondary infection is present."],
["Otalgia",
"Mild or absent. Significant pain suggests secondary acute infection."],
["Asymptomatic",
"Common, especially in children. Detected incidentally on otoscopy or school hearing screening."],
],
col_widths=[4.5*cm, W - 4.5*cm],
))
story.append(Spacer(1, 0.3*cm))
story.append(sub_header("Signs on Otoscopy"))
story.append(make_table(
["Stage", "Otoscopic Finding"],
[
["Stage I", "Slightly dull, indrawn pars tensa; normal light reflex may be altered."],
["Stage II", "TM touching long process of incus; incus appears prominent through thinned membrane."],
["Stage III","TM draped over promontory; 'disappearing' middle ear space; moves medially on Valsalva."],
["Stage IV", "Flat, featureless TM plastered to ossicles and promontory; no visible middle ear space."],
],
col_widths=[3.5*cm, W - 3.5*cm],
))
story.append(Spacer(1, 0.3*cm))
story.append(sub_header("Additional Otoscopic Findings"))
for b in [
"Tympanosclerosis (chalky white plaques) from collagen calcification",
"Amber/dull colouration if OME co-exists",
"<b>Retraction pockets</b> in pars tensa or pars flaccida — the most dangerous finding; precursor to "
"primary acquired cholesteatoma when non-self-cleaning",
"Ossicular landmarks (long process of incus, stapes) visible through thinned membrane",
]:
story.append(bullet_para(b))
story.append(Spacer(1, 0.3*cm))
# Otoscopy image
oto_img_path = img_dir + "otoscopy.png"
if os.path.exists(oto_img_path):
img = Image(oto_img_path, width=7*cm, height=7*cm)
img.hAlign = "CENTER"
story.append(img)
story.append(Paragraph(
"Fig. 2 — Endoscopic view of middle ear atelectasis with effusion. "
"Note the retracted, draped tympanic membrane overlying the middle ear structures. "
"(Source: Cummings Otolaryngology Head and Neck Surgery, Fig. 140.1)",
caption,
))
# ── 5. INVESTIGATIONS ─────────────────────────────────────────────────────
story.append(Spacer(1, 0.2*cm))
story.append(section_header("5. INVESTIGATIONS"))
story.append(Spacer(1, 0.3*cm))
investigations = [
("Otoscopy / Otoendoscopy / Pneumatoscopy",
"Gold standard for diagnosis and staging. Pneumatoscopy with Siegle speculum assesses TM mobility — "
"reduced/absent in atelectasis. Otoendoscopy provides magnification, clear staging, and assessment "
"of retraction pocket depth and self-cleaning status. Examination under microscope (EUM) is essential "
"pre-operatively."),
("Pure Tone Audiogram (PTA)",
"Typically demonstrates a conductive hearing loss with an air-bone gap. Degree of hearing loss correlates "
"with stage. Sensorineural component may occur in chronic disease."),
("Tympanometry",
"Type B (flat) curve when effusion is present; Type C (negative pressure peak) when ETD is present "
"without effusion. Absent stapedial reflexes in significant conductive loss."),
("Eustachian Tube Function Tests",
"Toynbee test (swallowing with nose pinched) and Valsalva manoeuvre for clinical assessment. "
"Sonotubometry and tubomanometry for objective measurement where available."),
("HRCT Temporal Bone",
"Indicated when: ossicular erosion is suspected (incus long process most vulnerable); retraction pocket "
"cannot be fully assessed; cholesteatoma is a differential; pre-operative planning is required. "
"Shows middle ear space obliteration, ossicular chain integrity, and mastoid pneumatisation."),
("MRI Temporal Bone (non-EPI DWI)",
"Diffusion-weighted imaging differentiates cholesteatoma from inflammatory tissue. Particularly valuable "
"for suspected cholesteatoma development from an atelectatic retraction pocket, or for recurrence "
"surveillance after surgery."),
]
for title_inv, desc in investigations:
story.append(sub_header(title_inv))
story.append(body_para(desc))
story.append(Spacer(1, 0.1*cm))
# ── 6. MANAGEMENT ─────────────────────────────────────────────────────────
story.append(section_header("6. MANAGEMENT"))
story.append(Spacer(1, 0.3*cm))
story.append(body_para(
"Management is guided by <b>stage, rate of progression, degree of hearing loss, ossicular integrity, "
"and presence of cholesteatoma risk</b>."
))
story.append(Spacer(1, 0.15*cm))
story.append(sub_header("A. Conservative Management (Stages I–II, Early Stage III)"))
for b in [
"<b>Treatment of underlying ETD:</b> Management of allergic rhinitis (nasal corticosteroids), adenoidal "
"hypertrophy, chronic rhinosinusitis, and GORD",
"<b>Autoinflation (Otovent balloon):</b> May improve ETD and partially reverse early retraction",
"<b>Watchful waiting with surveillance:</b> Stages I–II that are fully visible and self-cleaning may be "
"monitored with 3–6 monthly otoendoscopy and serial audiograms",
"<b>Hearing aids:</b> Temporising measure in bilateral disease with hearing loss pending surgery",
]:
story.append(bullet_para(b))
story.append(Spacer(1, 0.15*cm))
story.append(sub_header("B. Ventilation (Grommet / Tympanostomy) Tubes"))
story.append(body_para(
"Indicated for Stages I–III when ETD is the main driver and the TM is not yet adherent. Sadé (1992) "
"demonstrated improvement in atelectatic ears following ventilation tube insertion; Graham and Knight "
"reported reversal of atelectasis in selected cases. Tubes bypass the dysfunctional eustachian tube, "
"restore positive middle ear pressure, and allow the TM to return toward a more normal position. "
"Longer-term <b>T-tubes</b> are preferred in refractory or recurrent cases."
))
story.append(Spacer(1, 0.15*cm))
story.append(sub_header("C. Myringoplasty / Tympanoplasty"))
story.append(body_para(
"Indicated in Stages III–IV when significant hearing loss or ossicular erosion is present, when retraction "
"pockets are progressive or non-self-cleaning, or when grommet insertion has failed to reverse atelectasis. "
"The goal is reinforcement of the atelectatic / thinned tympanic membrane."
))
for b in [
"<b>Cartilage tympanoplasty</b> (cartilage shield or palisade technique) — cartilage is rigid and resists "
"re-retraction; provides durable long-term repair. Preferred over fascia alone in atelectasis.",
"Materials: tragal cartilage + perichondrium, temporalis fascia, or composite grafts",
"Endoscopic transcanal approach is increasingly used and has equivalent outcomes to microscopic approach",
]:
story.append(bullet_para(b))
story.append(Spacer(1, 0.15*cm))
story.append(sub_header("D. Ossiculoplasty"))
story.append(body_para(
"Indicated when ossicular erosion has occurred, most commonly involving the <b>long process of the incus</b> "
"or stapes superstructure. Performed concurrently with tympanoplasty."
))
for b in [
"Partial ossicular replacement prosthesis (PORP) — stapes superstructure intact",
"Total ossicular replacement prosthesis (TORP) — stapes superstructure absent, footplate present",
"Incus interposition — repositioning of eroded incus remnant",
]:
story.append(bullet_para(b))
story.append(Spacer(1, 0.15*cm))
story.append(sub_header("E. Management of Retraction Pockets"))
story.append(make_table(
["Pocket Type", "Management"],
[
["Self-cleaning, stable, fully visible",
"Surveillance with regular otoendoscopy (3-6 monthly). Serial audiograms."],
["Progressive, enlarging, non-self-cleaning",
"Surgical excision with reconstruction (tympanoplasty ± mastoidectomy)."],
["Keratin accumulating (cholesteatoma)",
"Canal wall up or canal wall down mastoidectomy depending on extent."],
],
col_widths=[5.5*cm, W - 5.5*cm],
))
story.append(Spacer(1, 0.15*cm))
story.append(sub_header("F. Mastoidectomy"))
story.append(body_para(
"Indicated when cholesteatoma has developed from an atelectatic retraction pocket. Also combined "
"with tympanoplasty in cases of poor mastoid pneumatisation to improve the long-term ventilation reservoir."
))
# ── 7. COMPLICATIONS ──────────────────────────────────────────────────────
story.append(Spacer(1, 0.2*cm))
story.append(section_header("7. COMPLICATIONS"))
story.append(Spacer(1, 0.3*cm))
story.append(make_table(
["Complication", "Mechanism"],
[
["Adhesive otitis media", "End-stage atelectasis (Stage IV); irreversible mucosal loss"],
["Conductive hearing loss", "Obliteration of middle ear space + ossicular erosion"],
["Sensorineural hearing loss", "Chronic middle ear inflammation extending to round window / inner ear"],
["Primary acquired cholesteatoma","Keratin accumulation in non-self-cleaning retraction pocket"],
["Ossicular erosion", "Pressure necrosis from retracted TM; incus long process most vulnerable"],
["Tympanosclerosis", "Collagen destruction and calcification within the tympanic membrane"],
["Labyrinthine fistula", "Cholesteatoma erosion of the lateral semicircular canal"],
["Facial nerve paralysis", "Cholesteatoma extension to fallopian canal"],
["Intracranial infection", "Advanced cholesteatoma with tegmental or sigmoid sinus erosion"],
],
col_widths=[5.5*cm, W - 5.5*cm],
))
# ── 8. SUMMARY ────────────────────────────────────────────────────────────
story.append(Spacer(1, 0.2*cm))
story.append(section_header("8. SUMMARY"))
story.append(Spacer(1, 0.3*cm))
story.append(body_para(
"Middle ear atelectasis is a progressive complication of eustachian tube dysfunction representing the medial "
"collapse of the tympanic membrane into the middle ear cleft. Using the Sadé-Berco classification "
"(Stages I–IV), it spans a spectrum from simple retraction to full adhesive otitis media. The critical "
"clinical concern is the transition from reversible atelectasis (Stage III) to irreversible adhesion "
"(Stage IV) and the development of cholesteatoma from progressive non-self-cleaning retraction pockets. "
"Management hinges on addressing the underlying ETD, ventilation tube insertion for early-to-moderate "
"disease, and cartilage tympanoplasty with or without ossiculoplasty for advanced disease. "
"<b>Early diagnosis and regular surveillance</b> are essential to prevent the most serious complications "
"of cholesteatoma formation and intracranial spread."
))
# Source line
story.append(Spacer(1, 0.4*cm))
story.append(HRFlowable(width=W, thickness=1, color=BORDER))
story.append(Paragraph(
"Sources: Cummings Otolaryngology Head and Neck Surgery, Chapter 140 (Complications of Otitis Media), "
"pp. 2681–2683 | K. J. Lee's Essential Otolaryngology | Sadé J, Berco E: Atelectasis and Secretory Otitis Media, "
"Ann Otol Rhinol Laryngol 85(Suppl 25):66–72, 1976",
source_style,
))
# ── Build PDF ────────────────────────────────────────────────────────────
doc.build(story)
print(f"PDF saved to: {OUTPUT_PATH}")
cd /home/daytona/workspace/atelectasis-essay && python generate_pdf.py 2>&1
ls -lh /home/daytona/workspace/atelectasis-essay/Atelectasis_Middle_Ear.pdf
Atelectasis Middle Ear
PDF Document · PDF