Anatomy of recurrent laryngeal nerve , superior laryngeal nerve, clinical significance with diagrams for Ent pg exam , make as pdf

Reading File
Reading File
Finding Sources
Finding Sources
Finding Sources
Reading File
Reading File
Reading File
Reading File
Reading File
Finding Sources
Reading File
Reading File
Reading File
Loading Image
Loading Image
Reading File
Loading Image
Reading File
Running Command

mkdir -p /home/daytona/workspace/laryngeal-nerves && ls /home/daytona/workspace/

Writing File

~/laryngeal-nerves/generate_pdf.py

#!/usr/bin/env python3
"""
Generate comprehensive PDF on Recurrent Laryngeal Nerve and Superior Laryngeal Nerve
for ENT PG Exam preparation.
"""

import requests
from reportlab.lib.pagesizes import A4
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.units import cm, mm
from reportlab.lib.colors import HexColor, black, white, Color
from reportlab.platypus import (
    SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
    HRFlowable, PageBreak, KeepTogether, Image as RLImage
)
from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_JUSTIFY, TA_RIGHT
from reportlab.platypus.flowables import Flowable
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
from io import BytesIO
import os

# ── Colors ──────────────────────────────────────────────────────────────────
DARK_BLUE   = HexColor('#1a3a5c')
MED_BLUE    = HexColor('#2563a8')
LIGHT_BLUE  = HexColor('#dbeafe')
TEAL        = HexColor('#0d7377')
TEAL_LIGHT  = HexColor('#d1f0f2')
ORANGE      = HexColor('#d97706')
ORANGE_LIGHT= HexColor('#fef3c7')
RED_DARK    = HexColor('#991b1b')
RED_LIGHT   = HexColor('#fee2e2')
GREEN_DARK  = HexColor('#065f46')
GREEN_LIGHT = HexColor('#d1fae5')
PURPLE_DARK = HexColor('#5b21b6')
PURPLE_LIGHT= HexColor('#ede9fe')
GRAY_BG     = HexColor('#f1f5f9')
GRAY_DARK   = HexColor('#374151')
GRAY_MED    = HexColor('#6b7280')
SLATE       = HexColor('#334155')
WHITE       = white

PAGE_W, PAGE_H = A4
L_MARGIN = R_MARGIN = 1.8*cm
T_MARGIN = 2*cm
B_MARGIN = 2*cm

# ── Styles ───────────────────────────────────────────────────────────────────
styles = getSampleStyleSheet()

def s(name, **kw):
    return ParagraphStyle(name, **kw)

TITLE_STYLE = s('DocTitle',
    fontSize=24, textColor=WHITE, fontName='Helvetica-Bold',
    alignment=TA_CENTER, spaceAfter=4, leading=30)

SUBTITLE_STYLE = s('DocSub',
    fontSize=13, textColor=HexColor('#bfdbfe'), fontName='Helvetica',
    alignment=TA_CENTER, spaceAfter=2, leading=18)

H1 = s('H1', fontSize=16, textColor=WHITE, fontName='Helvetica-Bold',
        alignment=TA_LEFT, spaceBefore=6, spaceAfter=4, leading=20)

H2 = s('H2', fontSize=13, textColor=DARK_BLUE, fontName='Helvetica-Bold',
        spaceBefore=10, spaceAfter=4, leading=17)

H3 = s('H3', fontSize=11, textColor=TEAL, fontName='Helvetica-Bold',
        spaceBefore=6, spaceAfter=3, leading=15)

BODY = s('Body', fontSize=10, textColor=SLATE, fontName='Helvetica',
         spaceBefore=3, spaceAfter=3, leading=14, alignment=TA_JUSTIFY)

BULLET = s('Bullet', fontSize=10, textColor=SLATE, fontName='Helvetica',
           spaceBefore=2, spaceAfter=2, leading=13, leftIndent=14,
           bulletIndent=4, alignment=TA_LEFT)

CAPTION = s('Caption', fontSize=8.5, textColor=GRAY_MED, fontName='Helvetica-Oblique',
            alignment=TA_CENTER, spaceBefore=2, spaceAfter=4, leading=12)

KEY_POINT = s('KeyPoint', fontSize=10, textColor=DARK_BLUE, fontName='Helvetica-Bold',
              spaceBefore=2, spaceAfter=2, leading=13, leftIndent=6)

EXAM_STYLE = s('Exam', fontSize=9.5, textColor=RED_DARK, fontName='Helvetica-Bold',
               spaceBefore=2, spaceAfter=2, leading=13)

NOTE_STYLE = s('Note', fontSize=9.5, textColor=GREEN_DARK, fontName='Helvetica',
               spaceBefore=2, spaceAfter=2, leading=13)

TABLE_HEADER = s('TH', fontSize=10, textColor=WHITE, fontName='Helvetica-Bold',
                 alignment=TA_CENTER, leading=13)

TABLE_CELL = s('TC', fontSize=9.5, textColor=SLATE, fontName='Helvetica',
               alignment=TA_LEFT, leading=13)

TABLE_CELL_C = s('TCC', fontSize=9.5, textColor=SLATE, fontName='Helvetica',
                 alignment=TA_CENTER, leading=13)

FOOTER_STYLE = s('Footer', fontSize=8, textColor=GRAY_MED, fontName='Helvetica',
                 alignment=TA_CENTER, leading=10)

# ── Helper flowables ─────────────────────────────────────────────────────────

class ColorBar(Flowable):
    """Full-width colored bar for section headers."""
    def __init__(self, text, bg=DARK_BLUE, text_style=None, height=28):
        super().__init__()
        self.text = text
        self.bg = bg
        self.text_style = text_style or H1
        self.bar_h = height
        self.width = PAGE_W - L_MARGIN - R_MARGIN

    def wrap(self, avail_w, avail_h):
        self.width = avail_w
        return avail_w, self.bar_h + 6

    def draw(self):
        c = self.canv
        c.saveState()
        c.setFillColor(self.bg)
        c.roundRect(0, 2, self.width, self.bar_h, 4, fill=1, stroke=0)
        c.setFillColor(WHITE)
        c.setFont('Helvetica-Bold', 13)
        c.drawString(10, self.bar_h/2 - 4, self.text)
        c.restoreState()


class SectionBox(Flowable):
    """Colored box for important info."""
    def __init__(self, text, bg=LIGHT_BLUE, border=MED_BLUE, text_color=DARK_BLUE,
                 icon='', font_size=10, width=None):
        super().__init__()
        self.text = text
        self.bg = bg
        self.border = border
        self.text_color = text_color
        self.icon = icon
        self.font_size = font_size
        self._width = width

    def wrap(self, avail_w, avail_h):
        self._avail_w = self._width or avail_w
        # estimate height
        chars_per_line = int(self._avail_w / (self.font_size * 0.55))
        lines = max(1, len(self.text) // max(1, chars_per_line) + 1)
        self._h = lines * (self.font_size + 4) + 16
        return self._avail_w, self._h

    def draw(self):
        c = self.canv
        c.saveState()
        w = self._avail_w
        h = self._h
        c.setFillColor(self.bg)
        c.setStrokeColor(self.border)
        c.setLineWidth(1.2)
        c.roundRect(0, 0, w, h, 5, fill=1, stroke=1)
        c.setFillColor(self.text_color)
        c.setFont('Helvetica', self.font_size)
        txt = (self.icon + '  ' if self.icon else '') + self.text
        # Simple text drawing
        c.drawString(10, h/2 - self.font_size/2 + 2, txt[:int(w/6)])
        c.restoreState()


def box_para(text, bg, border, text_color=None, icon=''):
    """Return a Table-based colored box paragraph."""
    tc = text_color or SLATE
    style = s(f'bp_{id(text)}', fontSize=10, textColor=tc, fontName='Helvetica',
              leading=14, spaceBefore=2, spaceAfter=2)
    cell_text = (f'<b>{icon}</b>  ' if icon else '') + text
    tbl = Table([[Paragraph(cell_text, style)]], 
                colWidths=[PAGE_W - L_MARGIN - R_MARGIN - 4])
    tbl.setStyle(TableStyle([
        ('BACKGROUND', (0,0), (-1,-1), bg),
        ('ROUNDEDCORNERS', [5]),
        ('BOX', (0,0), (-1,-1), 1, border),
        ('TOPPADDING', (0,0), (-1,-1), 7),
        ('BOTTOMPADDING', (0,0), (-1,-1), 7),
        ('LEFTPADDING', (0,0), (-1,-1), 10),
        ('RIGHTPADDING', (0,0), (-1,-1), 10),
    ]))
    return tbl


def bullet(text, indent=0):
    return Paragraph(f'<bullet>\u2022</bullet> {text}', BULLET)


def sub_bullet(text):
    st = s(f'sb_{id(text)}', fontSize=9.5, textColor=GRAY_DARK, fontName='Helvetica',
           spaceBefore=1, spaceAfter=1, leading=13, leftIndent=28, bulletIndent=18)
    return Paragraph(f'<bullet>\u2013</bullet> {text}', st)


def sp(n=1):
    return Spacer(1, n*4*mm)


def hr(color=MED_BLUE, thickness=0.8):
    return HRFlowable(width='100%', thickness=thickness, color=color, spaceAfter=3)


# ── Image download ────────────────────────────────────────────────────────────
def fetch_img(url, max_w, max_h):
    try:
        r = requests.get(url, timeout=15)
        r.raise_for_status()
        bio = BytesIO(r.content)
        img = RLImage(bio)
        iw, ih = img.imageWidth, img.imageHeight
        ratio = min(max_w/iw, max_h/ih)
        img.drawWidth = iw * ratio
        img.drawHeight = ih * ratio
        return img
    except Exception as e:
        print(f"Image fetch failed: {e}")
        return None


# ── Page template ─────────────────────────────────────────────────────────────
def on_page(canvas, doc):
    canvas.saveState()
    # Header bar
    canvas.setFillColor(DARK_BLUE)
    canvas.rect(0, PAGE_H - 1.1*cm, PAGE_W, 1.1*cm, fill=1, stroke=0)
    canvas.setFillColor(WHITE)
    canvas.setFont('Helvetica-Bold', 9)
    canvas.drawString(L_MARGIN, PAGE_H - 0.75*cm,
                      'Laryngeal Nerves — Anatomy & Clinical Significance  |  ENT PG Exam')
    canvas.setFont('Helvetica', 8)
    canvas.drawRightString(PAGE_W - R_MARGIN, PAGE_H - 0.75*cm, f'Page {doc.page}')

    # Footer
    canvas.setFillColor(GRAY_BG)
    canvas.rect(0, 0, PAGE_W, 1.0*cm, fill=1, stroke=0)
    canvas.setFillColor(GRAY_MED)
    canvas.setFont('Helvetica', 7.5)
    canvas.drawCentredString(PAGE_W/2, 0.35*cm,
        'Sources: Scott-Brown\'s ORL HNS | Cummings Otolaryngology | Bailey & Love | Gray\'s Anatomy | ENT PG Exam Notes')
    canvas.restoreState()


# ── Build content ─────────────────────────────────────────────────────────────
def build_pdf(out_path):
    doc = SimpleDocTemplate(
        out_path,
        pagesize=A4,
        leftMargin=L_MARGIN, rightMargin=R_MARGIN,
        topMargin=T_MARGIN + 1.1*cm, bottomMargin=B_MARGIN + 1.0*cm,
        title='Laryngeal Nerves — Anatomy & Clinical Significance',
        author='Orris Medical Education',
        subject='ENT PG Exam'
    )

    W = PAGE_W - L_MARGIN - R_MARGIN  # usable width
    story = []

    # ── COVER ──────────────────────────────────────────────────────────────────
    cover_tbl = Table(
        [[Paragraph('LARYNGEAL NERVES', TITLE_STYLE)],
         [Paragraph('Recurrent Laryngeal Nerve &amp; Superior Laryngeal Nerve', SUBTITLE_STYLE)],
         [Paragraph('Anatomy • Course • Branches • Clinical Significance', SUBTITLE_STYLE)],
         [sp(0.5)],
         [Paragraph('<b>ENT PG EXAMINATION NOTES</b>', 
                    s('cv', fontSize=12, textColor=HexColor('#93c5fd'), fontName='Helvetica-Bold',
                      alignment=TA_CENTER))],
         [Paragraph('Compiled from: Scott-Brown\'s ORL, Cummings Otolaryngology, Bailey &amp; Love, Gray\'s Anatomy',
                    s('cv2', fontSize=9, textColor=HexColor('#bfdbfe'), fontName='Helvetica',
                      alignment=TA_CENTER))],
        ],
        colWidths=[W]
    )
    cover_tbl.setStyle(TableStyle([
        ('BACKGROUND', (0,0), (-1,-1), DARK_BLUE),
        ('TOPPADDING', (0,0), (-1,-1), 14),
        ('BOTTOMPADDING', (0,0), (-1,-1), 14),
        ('LEFTPADDING', (0,0), (-1,-1), 20),
        ('RIGHTPADDING', (0,0), (-1,-1), 20),
        ('ROUNDEDCORNERS', [8]),
    ]))
    story += [cover_tbl, sp(2)]

    # Quick index table
    idx_data = [
        [Paragraph('<b>Section</b>', TABLE_HEADER), Paragraph('<b>Topic</b>', TABLE_HEADER)],
        [Paragraph('1', TABLE_CELL_C), Paragraph('Overview — Laryngeal Nerve Supply', TABLE_CELL)],
        [Paragraph('2', TABLE_CELL_C), Paragraph('Superior Laryngeal Nerve (SLN) — Anatomy &amp; Course', TABLE_CELL)],
        [Paragraph('3', TABLE_CELL_C), Paragraph('Recurrent Laryngeal Nerve (RLN) — Anatomy &amp; Course', TABLE_CELL)],
        [Paragraph('4', TABLE_CELL_C), Paragraph('Left vs Right RLN — Key Differences', TABLE_CELL)],
        [Paragraph('5', TABLE_CELL_C), Paragraph('Non-Recurrent Laryngeal Nerve', TABLE_CELL)],
        [Paragraph('6', TABLE_CELL_C), Paragraph('Galen\'s Anastomosis', TABLE_CELL)],
        [Paragraph('7', TABLE_CELL_C), Paragraph('Motor &amp; Sensory Distribution — Summary Table', TABLE_CELL)],
        [Paragraph('8', TABLE_CELL_C), Paragraph('SLN Injury — Clinical Features &amp; Causes', TABLE_CELL)],
        [Paragraph('9', TABLE_CELL_C), Paragraph('RLN Injury — Clinical Features &amp; Causes', TABLE_CELL)],
        [Paragraph('10', TABLE_CELL_C), Paragraph('Bilateral RLN Palsy', TABLE_CELL)],
        [Paragraph('11', TABLE_CELL_C), Paragraph('High-Yield Exam Points', TABLE_CELL)],
    ]
    idx_tbl = Table(idx_data, colWidths=[1.5*cm, W - 1.5*cm])
    idx_tbl.setStyle(TableStyle([
        ('BACKGROUND', (0,0), (-1,0), MED_BLUE),
        ('BACKGROUND', (0,1), (-1,1), LIGHT_BLUE),
        ('BACKGROUND', (0,2), (-1,2), WHITE),
        ('BACKGROUND', (0,3), (-1,3), LIGHT_BLUE),
        ('BACKGROUND', (0,4), (-1,4), WHITE),
        ('BACKGROUND', (0,5), (-1,5), LIGHT_BLUE),
        ('BACKGROUND', (0,6), (-1,6), WHITE),
        ('BACKGROUND', (0,7), (-1,7), LIGHT_BLUE),
        ('BACKGROUND', (0,8), (-1,8), WHITE),
        ('BACKGROUND', (0,9), (-1,9), LIGHT_BLUE),
        ('BACKGROUND', (0,10), (-1,10), WHITE),
        ('BACKGROUND', (0,11), (-1,11), LIGHT_BLUE),
        ('GRID', (0,0), (-1,-1), 0.5, HexColor('#cbd5e1')),
        ('TOPPADDING', (0,0), (-1,-1), 5),
        ('BOTTOMPADDING', (0,0), (-1,-1), 5),
        ('LEFTPADDING', (0,0), (-1,-1), 8),
        ('RIGHTPADDING', (0,0), (-1,-1), 8),
        ('ALIGN', (0,0), (0,-1), 'CENTER'),
        ('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
    ]))
    story += [Paragraph('Contents', H2), idx_tbl, PageBreak()]

    # ════════════════════════════════════════════════════════════════════════════
    # SECTION 1 — Overview
    # ════════════════════════════════════════════════════════════════════════════
    story.append(ColorBar('SECTION 1 — OVERVIEW: LARYNGEAL NERVE SUPPLY', DARK_BLUE))
    story.append(sp())

    story.append(Paragraph(
        'The motor and sensory innervation of the larynx is derived entirely from the <b>vagus nerve (CN X)</b> '
        'via two major branches:', BODY))
    story.append(sp(0.5))

    overview_data = [
        [Paragraph('<b>Nerve</b>', TABLE_HEADER),
         Paragraph('<b>Branch of</b>', TABLE_HEADER),
         Paragraph('<b>Motor Supply</b>', TABLE_HEADER),
         Paragraph('<b>Sensory Supply</b>', TABLE_HEADER)],
        [Paragraph('Superior Laryngeal Nerve (SLN)', TABLE_CELL),
         Paragraph('Inferior ganglion of vagus (+ sympathetic branch)', TABLE_CELL),
         Paragraph('Cricothyroid muscle only (via EBSLN)', TABLE_CELL),
         Paragraph('Larynx above vocal folds; epiglottis, vallecula, aryepiglottic fold (via IBSLN)', TABLE_CELL)],
        [Paragraph('Recurrent Laryngeal Nerve (RLN)', TABLE_CELL),
         Paragraph('Vagus nerve (in thorax)', TABLE_CELL),
         Paragraph('ALL intrinsic laryngeal muscles except cricothyroid', TABLE_CELL),
         Paragraph('Larynx below vocal folds (subglottis)', TABLE_CELL)],
    ]
    ov_tbl = Table(overview_data, colWidths=[W*0.22, W*0.22, W*0.28, W*0.28])
    ov_tbl.setStyle(TableStyle([
        ('BACKGROUND', (0,0), (-1,0), DARK_BLUE),
        ('BACKGROUND', (0,1), (-1,1), LIGHT_BLUE),
        ('BACKGROUND', (0,2), (-1,2), TEAL_LIGHT),
        ('GRID', (0,0), (-1,-1), 0.5, HexColor('#94a3b8')),
        ('TOPPADDING', (0,0), (-1,-1), 6),
        ('BOTTOMPADDING', (0,0), (-1,-1), 6),
        ('LEFTPADDING', (0,0), (-1,-1), 7),
        ('RIGHTPADDING', (0,0), (-1,-1), 7),
        ('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
    ]))
    story += [ov_tbl, sp()]

    # Key concept box
    story.append(box_para(
        'The RLN supplies ALL intrinsic laryngeal muscles EXCEPT cricothyroid. '
        'The cricothyroid is the only intrinsic laryngeal muscle supplied by the EXTERNAL branch of the SLN.',
        ORANGE_LIGHT, ORANGE, text_color=HexColor('#92400e'), icon='★ KEY'
    ))
    story.append(sp(2))

    # ── DIAGRAM ────────────────────────────────────────────────────────────────
    story.append(Paragraph('Diagram: Course of the Recurrent Laryngeal Nerve and Superior Laryngeal Nerve', H2))
    img1 = fetch_img(
        'https://cdn.orris.care/cdss_images/42574787ff65dfab5c42c99b2d0bda42f0a9c741a60bbc83ae93a6b2ee610ea9.png',
        W * 0.65, 14*cm)
    if img1:
        img_tbl = Table([[img1]], colWidths=[W])
        img_tbl.setStyle(TableStyle([
            ('ALIGN', (0,0), (-1,-1), 'CENTER'),
            ('TOPPADDING', (0,0), (-1,-1), 6),
            ('BOTTOMPADDING', (0,0), (-1,-1), 6),
        ]))
        story.append(img_tbl)
        story.append(Paragraph(
            'Fig. 1 — Course of the recurrent laryngeal nerve and the superior laryngeal nerve '
            '(Scott-Brown\'s Otorhinolaryngology Head &amp; Neck Surgery)', CAPTION))
    story.append(PageBreak())

    # ════════════════════════════════════════════════════════════════════════════
    # SECTION 2 — Superior Laryngeal Nerve
    # ════════════════════════════════════════════════════════════════════════════
    story.append(ColorBar('SECTION 2 — SUPERIOR LARYNGEAL NERVE (SLN)', TEAL))
    story.append(sp())

    story.append(Paragraph('2.1 Origin', H2))
    story.append(Paragraph(
        'The SLN arises from the <b>inferior (nodose) ganglion of the vagus nerve</b> in the '
        'upper part of the neck. It also receives a branch from the <b>superior cervical '
        'sympathetic ganglion</b>.', BODY))
    story.append(sp())

    story.append(Paragraph('2.2 Course', H2))
    story.append(Paragraph(
        'After arising from the inferior ganglion, the SLN descends lateral to the pharynx, '
        '<b>posterior to the internal carotid artery</b>. At the level of the '
        '<b>greater horn of the hyoid bone</b>, it divides into two branches:', BODY))
    story.append(sp(0.5))

    branches_data = [
        [Paragraph('<b>Branch</b>', TABLE_HEADER),
         Paragraph('<b>Size</b>', TABLE_HEADER),
         Paragraph('<b>Course</b>', TABLE_HEADER),
         Paragraph('<b>Function</b>', TABLE_HEADER)],
        [Paragraph('Internal Branch (IBSLN)', TABLE_CELL),
         Paragraph('Larger', TABLE_CELL),
         Paragraph(
             'Pierces the thyrohyoid membrane above the entry of the superior laryngeal artery. '
             'Divides into upper and lower branches beneath the mucosa of the piriform fossa.',
             TABLE_CELL),
         Paragraph(
             'SENSORY + secretomotor: supplies mucosa of epiglottis, vallecula, aryepiglottic fold, '
             'laryngeal vestibule, and larynx down to the level of vocal folds.',
             TABLE_CELL)],
        [Paragraph('External Branch (EBSLN)', TABLE_CELL),
         Paragraph('Smaller', TABLE_CELL),
         Paragraph(
             'Runs along the inferior pharyngeal constrictor, descends to cricothyroid muscle. '
             'Up to 20% lie subfascially and are visually unidentifiable.',
             TABLE_CELL),
         Paragraph(
             'MOTOR: cricothyroid muscle only (lengthens and tenses vocal folds — controls pitch).',
             TABLE_CELL)],
    ]
    br_tbl = Table(branches_data, colWidths=[W*0.20, W*0.10, W*0.38, W*0.32])
    br_tbl.setStyle(TableStyle([
        ('BACKGROUND', (0,0), (-1,0), TEAL),
        ('BACKGROUND', (0,1), (-1,1), TEAL_LIGHT),
        ('BACKGROUND', (0,2), (-1,2), WHITE),
        ('GRID', (0,0), (-1,-1), 0.5, HexColor('#94a3b8')),
        ('TOPPADDING', (0,0), (-1,-1), 6),
        ('BOTTOMPADDING', (0,0), (-1,-1), 6),
        ('LEFTPADDING', (0,0), (-1,-1), 7),
        ('RIGHTPADDING', (0,0), (-1,-1), 7),
        ('VALIGN', (0,0), (-1,-1), 'TOP'),
    ]))
    story += [br_tbl, sp()]

    story.append(Paragraph('2.3 Internal Branch — Details', H2))
    story.append(Paragraph(
        'The internal branch pierces the thyrohyoid membrane just above where the '
        '<b>superior laryngeal artery</b> enters. It divides into two sensory branches:', BODY))
    story.append(bullet('<b>Upper branch:</b> Supplies mucosa above the ventricle (laryngeal vestibule and epiglottis)'))
    story.append(bullet('<b>Lower branch:</b> Runs in the medial wall of the piriform fossa beneath the mucosa; '
                        'supplies the aryepiglottic fold and laryngeal mucosa down to the vocal folds'))
    story.append(Paragraph(
        'The internal branch terminates by <b>piercing the inferior constrictor</b> and uniting with an '
        'ascending branch of the RLN — this anastomosis is called <b>Galen\'s anastomosis</b> (purely sensory).', BODY))
    story.append(sp())

    story.append(box_para(
        'Clinical pearl: The internal branch of the SLN lies in the medial wall of the piriform fossa, '
        'accessible for local anaesthetic injection — provides excellent anaesthesia for most of the piriform fossa.',
        TEAL_LIGHT, TEAL, text_color=HexColor('#064e3b'), icon='✦ CLINICAL'
    ))
    story.append(sp())

    story.append(Paragraph('2.4 Joll\'s Triangle (Sternothyrolaryngeal Triangle)', H2))
    story.append(Paragraph(
        'The EBSLN is identified during thyroid surgery at <b>Joll\'s triangle</b>, which is delineated by:', BODY))
    story.append(bullet('Superior thyroid pedicle (superior boundary)'))
    story.append(bullet('Upper lobe of the thyroid gland'))
    story.append(bullet('Cricothyroid muscle (inferior-medial)'))
    story.append(bullet('Lower edge of the thyroid cartilage'))
    story.append(sp(2))

    # ════════════════════════════════════════════════════════════════════════════
    # SECTION 3 — Recurrent Laryngeal Nerve
    # ════════════════════════════════════════════════════════════════════════════
    story.append(ColorBar('SECTION 3 — RECURRENT LARYNGEAL NERVE (RLN)', MED_BLUE))
    story.append(sp())

    story.append(Paragraph('3.1 Origin', H2))
    story.append(Paragraph(
        'The RLN is a branch of the <b>vagus nerve (CN X)</b>. The nerve originates at different '
        'anatomical levels on the left and right sides due to embryological differences in the '
        'development of the aortic arches.', BODY))
    story.append(sp())

    story.append(Paragraph('3.2 Right Recurrent Laryngeal Nerve', H2))
    story.append(Paragraph(
        'The right RLN branches from the right vagus nerve as it crosses superficial to the '
        '<b>first part of the right subclavian artery</b> in the root of the neck.', BODY))
    story.append(bullet('It passes <b>around (under) the right subclavian artery</b>'))
    story.append(bullet('Ascends in the <b>tracheoesophageal groove</b>'))
    story.append(bullet('Course is more oblique — not strictly in the tracheoesophageal groove for its entire course'))
    story.append(bullet('Passes deep to the lower border of the inferior constrictor'))
    story.append(bullet('Enters the larynx behind the <b>cricothyroid joint</b>'))
    story.append(sp())

    story.append(Paragraph('3.3 Left Recurrent Laryngeal Nerve', H2))
    story.append(Paragraph(
        'The left RLN branches from the left vagus nerve as it crosses the <b>arch of the aorta</b> '
        'in the superior mediastinum.', BODY))
    story.append(bullet('Passes below and behind the <b>arch of the aorta</b>'))
    story.append(bullet('Loops around the aorta at the level of the <b>ligamentum arteriosum</b>'))
    story.append(bullet('Ascends in the <b>tracheoesophageal groove</b> on the left side'))
    story.append(bullet('Accompanied by the laryngeal branch of the <b>inferior thyroid artery</b>'))
    story.append(bullet('Passes deep to the lower border of the inferior constrictor'))
    story.append(bullet('Enters the larynx behind the <b>cricothyroid joint</b>'))
    story.append(sp())

    story.append(Paragraph('3.4 Branches of the RLN', H2))
    story.append(Paragraph(
        'Near or after entry into the larynx, the RLN divides into <b>motor</b> and <b>sensory</b> branches:', BODY))
    story.append(bullet('<b>Motor branch:</b> Contains fibres derived from the cranial root of the '
                        'accessory nerve — supplies ALL intrinsic laryngeal muscles EXCEPT cricothyroid'))
    story.append(bullet('<b>Sensory branch:</b> Supplies laryngeal mucosa below the level of the vocal folds; '
                        'also carries afferent fibres from stretch receptors in the larynx'))
    story.append(sp())

    story.append(box_para(
        'Up to 30% of RLNs branch BEFORE entering the larynx (extralaryngeal branching) — '
        'this increases the risk of inadvertent surgical injury.',
        ORANGE_LIGHT, ORANGE, text_color=HexColor('#92400e'), icon='⚠ IMPORTANT'
    ))
    story.append(sp())

    story.append(Paragraph('3.5 Relations in the Neck (Behrs Triangle)', H2))
    story.append(Paragraph(
        'In the neck, the RLN can be identified using the <b>Behrs triangle</b>, defined by:', BODY))
    story.append(bullet('<b>Superior border:</b> Inferior thyroid artery'))
    story.append(bullet('<b>Medial border:</b> Trachea'))
    story.append(bullet('<b>Lateral border:</b> Common carotid artery'))
    story.append(Paragraph(
        'The nerve accompanies the <b>laryngeal branch of the inferior thyroid artery</b> '
        'as it ascends in the tracheoesophageal groove.', BODY))
    story.append(sp())

    story.append(Paragraph('3.6 Relationship to Berry\'s Ligament', H2))
    story.append(Paragraph(
        'The RLN has a close relationship with <b>Berry\'s ligament</b> (posterior suspensory ligament '
        'of the thyroid). In patients with a <b>low Berry\'s ligament</b> (sitting posterior on the '
        'trachea), the nerve is in close proximity to the thyroid gland — greatest risk of injury '
        'during thyroidectomy. When Berry\'s ligament is high, the nerve is more laterally placed '
        'with less risk.', BODY))
    story.append(PageBreak())

    # ════════════════════════════════════════════════════════════════════════════
    # SECTION 4 — Left vs Right Differences
    # ════════════════════════════════════════════════════════════════════════════
    story.append(ColorBar('SECTION 4 — LEFT vs RIGHT RLN: KEY DIFFERENCES', PURPLE_DARK))
    story.append(sp())

    diff_data = [
        [Paragraph('<b>Feature</b>', TABLE_HEADER),
         Paragraph('<b>Right RLN</b>', TABLE_HEADER),
         Paragraph('<b>Left RLN</b>', TABLE_HEADER)],
        [Paragraph('Origin', TABLE_CELL),
         Paragraph('Loops around <b>right subclavian artery</b>', TABLE_CELL),
         Paragraph('Loops around <b>arch of aorta</b> at ligamentum arteriosum', TABLE_CELL)],
        [Paragraph('Level of loop', TABLE_CELL),
         Paragraph('Root of neck (higher level)', TABLE_CELL),
         Paragraph('Superior mediastinum (lower level)', TABLE_CELL)],
        [Paragraph('Course length', TABLE_CELL),
         Paragraph('Shorter', TABLE_CELL),
         Paragraph('Longer', TABLE_CELL)],
        [Paragraph('Tracheoesophageal groove', TABLE_CELL),
         Paragraph('Less strictly in groove (more oblique)', TABLE_CELL),
         Paragraph('Strictly in tracheoesophageal groove', TABLE_CELL)],
        [Paragraph('Non-recurrent nerve', TABLE_CELL),
         Paragraph('~2% (associated with retroesophageal subclavian artery or situs inversus)', TABLE_CELL),
         Paragraph('Extremely rare', TABLE_CELL)],
        [Paragraph('Mediastinal causes of palsy', TABLE_CELL),
         Paragraph('Apex of right lung cancer, subclavian artery aneurysm', TABLE_CELL),
         Paragraph('Aortopulmonary window (lung cancer, lymph nodes, aortic arch aneurysm)', TABLE_CELL)],
        [Paragraph('Surgical risk', TABLE_CELL),
         Paragraph('Non-recurrent variant — must be anticipated', TABLE_CELL),
         Paragraph('Longer course — more vulnerable to mediastinal pathology', TABLE_CELL)],
    ]
    diff_tbl = Table(diff_data, colWidths=[W*0.28, W*0.36, W*0.36])
    diff_tbl.setStyle(TableStyle([
        ('BACKGROUND', (0,0), (-1,0), PURPLE_DARK),
        ('BACKGROUND', (0,1), (-1,1), PURPLE_LIGHT),
        ('BACKGROUND', (0,2), (-1,2), WHITE),
        ('BACKGROUND', (0,3), (-1,3), PURPLE_LIGHT),
        ('BACKGROUND', (0,4), (-1,4), WHITE),
        ('BACKGROUND', (0,5), (-1,5), PURPLE_LIGHT),
        ('BACKGROUND', (0,6), (-1,6), WHITE),
        ('BACKGROUND', (0,7), (-1,7), PURPLE_LIGHT),
        ('GRID', (0,0), (-1,-1), 0.5, HexColor('#94a3b8')),
        ('TOPPADDING', (0,0), (-1,-1), 6),
        ('BOTTOMPADDING', (0,0), (-1,-1), 6),
        ('LEFTPADDING', (0,0), (-1,-1), 7),
        ('RIGHTPADDING', (0,0), (-1,-1), 7),
        ('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
    ]))
    story += [diff_tbl, sp(2)]

    # ════════════════════════════════════════════════════════════════════════════
    # SECTION 5 — Non-Recurrent Laryngeal Nerve
    # ════════════════════════════════════════════════════════════════════════════
    story.append(ColorBar('SECTION 5 — NON-RECURRENT LARYNGEAL NERVE (NRLN)', HexColor('#7c3aed')))
    story.append(sp())

    story.append(Paragraph(
        'Around <b>2% of RLNs are non-recurrent</b> and this is almost always on the <b>right side</b>. '
        'This is a critical anatomical variant for surgeons.', BODY))
    story.append(sp())

    nrln_data = [
        [Paragraph('<b>Feature</b>', TABLE_HEADER), Paragraph('<b>Details</b>', TABLE_HEADER)],
        [Paragraph('Incidence', TABLE_CELL), Paragraph('~2% overall; almost exclusively RIGHT side', TABLE_CELL)],
        [Paragraph('Associated anomaly', TABLE_CELL),
         Paragraph('<b>Retroesophageal (aberrant) subclavian artery</b> (arteria lusoria) or situs inversus', TABLE_CELL)],
        [Paragraph('Course', TABLE_CELL),
         Paragraph('Runs inferomedially directly from the vagus nerve — does NOT loop around the subclavian artery', TABLE_CELL)],
        [Paragraph('Appearance', TABLE_CELL),
         Paragraph('Wider/larger than normal RLN', TABLE_CELL)],
        [Paragraph('Surgical risk', TABLE_CELL),
         Paragraph('Extremely high risk of injury if not anticipated — nerve courses directly into surgical field', TABLE_CELL)],
        [Paragraph('How to suspect', TABLE_CELL),
         Paragraph('Pre-operative imaging showing retroesophageal subclavian artery; nerve not found in normal position', TABLE_CELL)],
        [Paragraph('Approach if suspected', TABLE_CELL),
         Paragraph('Use superior approach; identify nerve at cricothyroid junction before dissecting caudally', TABLE_CELL)],
    ]
    nrln_tbl = Table(nrln_data, colWidths=[W*0.28, W*0.72])
    nrln_tbl.setStyle(TableStyle([
        ('BACKGROUND', (0,0), (-1,0), HexColor('#7c3aed')),
        ('BACKGROUND', (0,1), (-1,1), PURPLE_LIGHT),
        ('BACKGROUND', (0,2), (-1,2), WHITE),
        ('BACKGROUND', (0,3), (-1,3), PURPLE_LIGHT),
        ('BACKGROUND', (0,4), (-1,4), WHITE),
        ('BACKGROUND', (0,5), (-1,5), RED_LIGHT),
        ('BACKGROUND', (0,6), (-1,6), WHITE),
        ('BACKGROUND', (0,7), (-1,7), GREEN_LIGHT),
        ('GRID', (0,0), (-1,-1), 0.5, HexColor('#94a3b8')),
        ('TOPPADDING', (0,0), (-1,-1), 6),
        ('BOTTOMPADDING', (0,0), (-1,-1), 6),
        ('LEFTPADDING', (0,0), (-1,-1), 7),
        ('RIGHTPADDING', (0,0), (-1,-1), 7),
        ('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
    ]))
    story += [nrln_tbl, sp(2)]

    # ════════════════════════════════════════════════════════════════════════════
    # SECTION 6 — Galen's Anastomosis
    # ════════════════════════════════════════════════════════════════════════════
    story.append(ColorBar('SECTION 6 — GALEN\'S ANASTOMOSIS', HexColor('#0f766e')))
    story.append(sp())

    story.append(Paragraph(
        '<b>Galen\'s anastomosis</b> is the communication between the <b>internal branch of the SLN</b> '
        'and an <b>ascending branch of the RLN</b>.', BODY))
    story.append(sp())

    galen_data = [
        [Paragraph('<b>Feature</b>', TABLE_HEADER), Paragraph('<b>Details</b>', TABLE_HEADER)],
        [Paragraph('Components', TABLE_CELL),
         Paragraph('Terminal branch of IBSLN + ascending branch of RLN', TABLE_CELL)],
        [Paragraph('Location', TABLE_CELL),
         Paragraph('Within the inferior constrictor muscle of the pharynx', TABLE_CELL)],
        [Paragraph('Nature', TABLE_CELL),
         Paragraph('Purely SENSORY anastomosis', TABLE_CELL)],
        [Paragraph('Function', TABLE_CELL),
         Paragraph('Sensory supply to subglottis and lower larynx; also implicated in innervation of thyroarytenoid in some studies', TABLE_CELL)],
        [Paragraph('Clinical relevance', TABLE_CELL),
         Paragraph('Combined SLN + RLN injury produces more severe deficit than either alone due to ipsilateral sensory loss', TABLE_CELL)],
        [Paragraph('Human communicating nerve', TABLE_CELL),
         Paragraph('An anastomosis between EBSLN and RLN seen in 70% of human larynges; provides sensory supply to subglottis and motor to thyroarytenoid', TABLE_CELL)],
    ]
    galen_tbl = Table(galen_data, colWidths=[W*0.28, W*0.72])
    galen_tbl.setStyle(TableStyle([
        ('BACKGROUND', (0,0), (-1,0), HexColor('#0f766e')),
        ('BACKGROUND', (0,1), (-1,1), TEAL_LIGHT),
        ('BACKGROUND', (0,2), (-1,2), WHITE),
        ('BACKGROUND', (0,3), (-1,3), TEAL_LIGHT),
        ('BACKGROUND', (0,4), (-1,4), WHITE),
        ('BACKGROUND', (0,5), (-1,5), TEAL_LIGHT),
        ('BACKGROUND', (0,6), (-1,6), ORANGE_LIGHT),
        ('GRID', (0,0), (-1,-1), 0.5, HexColor('#94a3b8')),
        ('TOPPADDING', (0,0), (-1,-1), 6),
        ('BOTTOMPADDING', (0,0), (-1,-1), 6),
        ('LEFTPADDING', (0,0), (-1,-1), 7),
        ('RIGHTPADDING', (0,0), (-1,-1), 7),
        ('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
    ]))
    story += [galen_tbl, PageBreak()]

    # ════════════════════════════════════════════════════════════════════════════
    # SECTION 7 — Motor & Sensory Distribution Summary
    # ════════════════════════════════════════════════════════════════════════════
    story.append(ColorBar('SECTION 7 — MOTOR & SENSORY DISTRIBUTION SUMMARY', MED_BLUE))
    story.append(sp())

    story.append(Paragraph('7.1 Intrinsic Laryngeal Muscles and Innervation', H2))
    muscles_data = [
        [Paragraph('<b>Muscle</b>', TABLE_HEADER),
         Paragraph('<b>Nerve Supply</b>', TABLE_HEADER),
         Paragraph('<b>Action</b>', TABLE_HEADER)],
        [Paragraph('Cricothyroid', TABLE_CELL),
         Paragraph('External branch of SLN', TABLE_CELL),
         Paragraph('Lengthens and tenses vocal folds (increases pitch)', TABLE_CELL)],
        [Paragraph('Posterior cricoarytenoid (PCA)', TABLE_CELL),
         Paragraph('RLN', TABLE_CELL),
         Paragraph('<b>ONLY abductor</b> of vocal folds — opens glottis during respiration', TABLE_CELL)],
        [Paragraph('Lateral cricoarytenoid (LCA)', TABLE_CELL),
         Paragraph('RLN', TABLE_CELL),
         Paragraph('Adducts vocal folds', TABLE_CELL)],
        [Paragraph('Transverse + Oblique Arytenoid', TABLE_CELL),
         Paragraph('RLN', TABLE_CELL),
         Paragraph('Adducts (closes glottis); only unpaired intrinsic muscle', TABLE_CELL)],
        [Paragraph('Thyroarytenoid (TA)', TABLE_CELL),
         Paragraph('RLN (+ possible EBSLN via communicating nerve)', TABLE_CELL),
         Paragraph('Shortens and relaxes vocal folds; adducts', TABLE_CELL)],
        [Paragraph('Vocalis (medial TA)', TABLE_CELL),
         Paragraph('RLN', TABLE_CELL),
         Paragraph('Fine tension adjustment of vocal folds', TABLE_CELL)],
        [Paragraph('Aryepiglottic', TABLE_CELL),
         Paragraph('RLN', TABLE_CELL),
         Paragraph('Narrows laryngeal inlet during swallowing', TABLE_CELL)],
    ]
    muscles_tbl = Table(muscles_data, colWidths=[W*0.28, W*0.28, W*0.44])
    muscles_tbl.setStyle(TableStyle([
        ('BACKGROUND', (0,0), (-1,0), MED_BLUE),
        ('BACKGROUND', (0,1), (-1,1), ORANGE_LIGHT),
        ('ROWBACKGROUNDS', (0,2), (-1,-1), [LIGHT_BLUE, WHITE]),
        ('GRID', (0,0), (-1,-1), 0.5, HexColor('#94a3b8')),
        ('TOPPADDING', (0,0), (-1,-1), 6),
        ('BOTTOMPADDING', (0,0), (-1,-1), 6),
        ('LEFTPADDING', (0,0), (-1,-1), 7),
        ('RIGHTPADDING', (0,0), (-1,-1), 7),
        ('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
    ]))
    story += [muscles_tbl, sp()]

    story.append(box_para(
        'MEMORY AID — "All muscles RLN EXCEPT CricoThyroid by External SLN (CTES)." '
        'The Posterior CricoArytenoid (PCA) is the ONLY abductor — "PCA opens the glottis."',
        ORANGE_LIGHT, ORANGE, text_color=HexColor('#92400e'), icon='★ MNEMONIC'
    ))
    story.append(sp())

    story.append(Paragraph('7.2 Sensory Distribution', H2))
    sense_data = [
        [Paragraph('<b>Region</b>', TABLE_HEADER), Paragraph('<b>Nerve</b>', TABLE_HEADER)],
        [Paragraph('Epiglottis (lingual surface)', TABLE_CELL),
         Paragraph('Internal branch of SLN + Glossopharyngeal nerve', TABLE_CELL)],
        [Paragraph('Epiglottis (laryngeal surface), vallecula', TABLE_CELL),
         Paragraph('Internal branch of SLN', TABLE_CELL)],
        [Paragraph('Aryepiglottic fold', TABLE_CELL),
         Paragraph('Internal branch of SLN (lower branch)', TABLE_CELL)],
        [Paragraph('Laryngeal vestibule (above vocal folds)', TABLE_CELL),
         Paragraph('Internal branch of SLN', TABLE_CELL)],
        [Paragraph('True vocal folds', TABLE_CELL),
         Paragraph('SLN (above) + RLN (below) — the vocal fold straddles both territories', TABLE_CELL)],
        [Paragraph('Subglottis (below vocal folds)', TABLE_CELL),
         Paragraph('Recurrent laryngeal nerve', TABLE_CELL)],
        [Paragraph('Piriform fossa', TABLE_CELL),
         Paragraph('Internal branch of SLN (accessible for LA injection here)', TABLE_CELL)],
    ]
    sense_tbl = Table(sense_data, colWidths=[W*0.48, W*0.52])
    sense_tbl.setStyle(TableStyle([
        ('BACKGROUND', (0,0), (-1,0), TEAL),
        ('ROWBACKGROUNDS', (0,1), (-1,-1), [TEAL_LIGHT, WHITE]),
        ('GRID', (0,0), (-1,-1), 0.5, HexColor('#94a3b8')),
        ('TOPPADDING', (0,0), (-1,-1), 6),
        ('BOTTOMPADDING', (0,0), (-1,-1), 6),
        ('LEFTPADDING', (0,0), (-1,-1), 7),
        ('RIGHTPADDING', (0,0), (-1,-1), 7),
        ('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
    ]))
    story += [sense_tbl, PageBreak()]

    # ════════════════════════════════════════════════════════════════════════════
    # SECTION 8 — SLN Clinical Significance
    # ════════════════════════════════════════════════════════════════════════════
    story.append(ColorBar('SECTION 8 — SLN INJURY: CLINICAL SIGNIFICANCE', TEAL))
    story.append(sp())

    story.append(Paragraph('8.1 External Branch (EBSLN) Injury', H2))
    story.append(Paragraph(
        'Injury to the EBSLN is the most common SLN injury (incidence up to <b>25% in thyroid surgery</b>). '
        'It is often unrecognized.', BODY))
    story.append(sp(0.5))

    ebsln_data = [
        [Paragraph('<b>Feature</b>', TABLE_HEADER), Paragraph('<b>Details</b>', TABLE_HEADER)],
        [Paragraph('Mechanism of injury', TABLE_CELL),
         Paragraph('Thyroidectomy — ligation of superior thyroid vessels on thyroid capsule (not individually). '
                   '20% of EBSLN are subfascial and visually unidentifiable.', TABLE_CELL)],
        [Paragraph('Effect', TABLE_CELL),
         Paragraph('Loss of cricothyroid function → inability to lengthen/tense vocal folds', TABLE_CELL)],
        [Paragraph('Voice changes', TABLE_CELL),
         Paragraph('Difficulty shouting, projecting voice; singers lose high-pitch range; voice fatigue', TABLE_CELL)],
        [Paragraph('Laryngoscopy', TABLE_CELL),
         Paragraph('Vocal cord appears shortened, bowed; rotation of glottis to injured side; '
                   'asymmetry of cords on phonation', TABLE_CELL)],
        [Paragraph('Assessment', TABLE_CELL),
         Paragraph('Videostroboscopy + formal voice evaluation + speech therapy', TABLE_CELL)],
        [Paragraph('Prevention', TABLE_CELL),
         Paragraph('Identify EBSLN in Joll\'s triangle; ligate superior pole vessels individually on thyroid capsule; '
                   'intraoperative nerve monitoring (IONM)', TABLE_CELL)],
        [Paragraph('Treatment', TABLE_CELL),
         Paragraph('Voice therapy; largely compensatory', TABLE_CELL)],
    ]
    ebsln_tbl = Table(ebsln_data, colWidths=[W*0.25, W*0.75])
    ebsln_tbl.setStyle(TableStyle([
        ('BACKGROUND', (0,0), (-1,0), TEAL),
        ('ROWBACKGROUNDS', (0,1), (-1,-1), [TEAL_LIGHT, WHITE]),
        ('GRID', (0,0), (-1,-1), 0.5, HexColor('#94a3b8')),
        ('TOPPADDING', (0,0), (-1,-1), 6),
        ('BOTTOMPADDING', (0,0), (-1,-1), 6),
        ('LEFTPADDING', (0,0), (-1,-1), 7),
        ('RIGHTPADDING', (0,0), (-1,-1), 7),
        ('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
    ]))
    story += [ebsln_tbl, sp()]

    story.append(Paragraph('8.2 Internal Branch (IBSLN) Injury', H2))
    story.append(Paragraph(
        'The internal branch is a purely sensory nerve. Injury leads to:', BODY))
    story.append(bullet('Loss of sensation in the supraglottis, epiglottis, and piriform fossa'))
    story.append(bullet('<b>Aspiration</b> — especially of liquids (loss of cough reflex in that region)'))
    story.append(bullet('Loss of afferent limb of the laryngeal adductor reflex'))
    story.append(bullet(
        'If the inferior branch is damaged during supraglottic resection — '
        'aspiration risk increases significantly'))
    story.append(sp())

    story.append(Paragraph('8.3 Causes of SLN Injury', H2))
    sln_causes_data = [
        [Paragraph('<b>Category</b>', TABLE_HEADER), Paragraph('<b>Causes</b>', TABLE_HEADER)],
        [Paragraph('Surgical', TABLE_CELL),
         Paragraph('Thyroidectomy (most common), laryngectomy, supraglottic resection, '
                   'carotid endarterectomy, anterior cervical spine surgery', TABLE_CELL)],
        [Paragraph('Iatrogenic', TABLE_CELL),
         Paragraph('Prolonged intubation, external neck manipulation', TABLE_CELL)],
        [Paragraph('Neoplastic', TABLE_CELL),
         Paragraph('Pharyngeal/hypopharyngeal carcinoma, thyroid cancer, '
                   'cervical lymph node metastasis', TABLE_CELL)],
        [Paragraph('Other', TABLE_CELL),
         Paragraph('Neck trauma, viral neuropathy (e.g., post-viral neuritis)', TABLE_CELL)],
    ]
    sln_causes_tbl = Table(sln_causes_data, colWidths=[W*0.22, W*0.78])
    sln_causes_tbl.setStyle(TableStyle([
        ('BACKGROUND', (0,0), (-1,0), TEAL),
        ('ROWBACKGROUNDS', (0,1), (-1,-1), [TEAL_LIGHT, WHITE]),
        ('GRID', (0,0), (-1,-1), 0.5, HexColor('#94a3b8')),
        ('TOPPADDING', (0,0), (-1,-1), 6),
        ('BOTTOMPADDING', (0,0), (-1,-1), 6),
        ('LEFTPADDING', (0,0), (-1,-1), 7),
        ('RIGHTPADDING', (0,0), (-1,-1), 7),
        ('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
    ]))
    story += [sln_causes_tbl, PageBreak()]

    # ════════════════════════════════════════════════════════════════════════════
    # SECTION 9 — RLN Clinical Significance
    # ════════════════════════════════════════════════════════════════════════════
    story.append(ColorBar('SECTION 9 — RLN INJURY: CLINICAL SIGNIFICANCE', MED_BLUE))
    story.append(sp())

    story.append(Paragraph('9.1 Causes of RLN Palsy', H2))

    rln_causes_data = [
        [Paragraph('<b>Category</b>', TABLE_HEADER),
         Paragraph('<b>Right RLN</b>', TABLE_HEADER),
         Paragraph('<b>Left RLN</b>', TABLE_HEADER)],
        [Paragraph('Surgical', TABLE_CELL),
         Paragraph('Thyroidectomy, parathyroidectomy, anterior cervical spine surgery, '
                   'carotid endarterectomy', TABLE_CELL),
         Paragraph('Thyroidectomy, thoracic surgery (CABG, aortic arch repair, '
                   'oesophagectomy, pneumonectomy)', TABLE_CELL)],
        [Paragraph('Neoplastic', TABLE_CELL),
         Paragraph('Thyroid carcinoma, apex of right lung cancer, '
                   'subclavian artery aneurysm, cervical lymphadenopathy', TABLE_CELL),
         Paragraph('<b>Aortopulmonary window</b> — lung cancer (most common cause of '
                   'left VCP), lymphoma, mediastinal tumours, aortic arch aneurysm', TABLE_CELL)],
        [Paragraph('Inflammatory', TABLE_CELL),
         Paragraph('Cervical lymphadenopathy, thyroiditis', TABLE_CELL),
         Paragraph('Mediastinal lymphadenopathy (TB, sarcoid), pericarditis', TABLE_CELL)],
        [Paragraph('Cardiac', TABLE_CELL),
         Paragraph('Subclavian artery aneurysm', TABLE_CELL),
         Paragraph('Mitral stenosis (Ortner\'s syndrome — cardiovocal syndrome), '
                   'aortic aneurysm', TABLE_CELL)],
        [Paragraph('Idiopathic', TABLE_CELL),
         Paragraph('Viral neuritis', TABLE_CELL),
         Paragraph('Viral neuritis', TABLE_CELL)],
        [Paragraph('Neurological', TABLE_CELL),
         Paragraph('Vagal lesions above RLN origin (skull base tumours, '
                   'jugular foramen lesions)', TABLE_CELL),
         Paragraph('Same — + brainstem lesions', TABLE_CELL)],
    ]
    rln_causes_tbl = Table(rln_causes_data, colWidths=[W*0.18, W*0.41, W*0.41])
    rln_causes_tbl.setStyle(TableStyle([
        ('BACKGROUND', (0,0), (-1,0), MED_BLUE),
        ('ROWBACKGROUNDS', (0,1), (-1,-1), [LIGHT_BLUE, WHITE]),
        ('GRID', (0,0), (-1,-1), 0.5, HexColor('#94a3b8')),
        ('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), 'MIDDLE'),
    ]))
    story += [rln_causes_tbl, sp()]

    story.append(Paragraph('9.2 Clinical Features of Unilateral RLN Palsy', H2))
    story.append(Paragraph(
        'Vocal cord lies in the <b>paramedian position</b>. The contralateral cord usually '
        'compensates over time.', BODY))
    story.append(sp(0.5))

    uni_data = [
        [Paragraph('<b>Feature</b>', TABLE_HEADER), Paragraph('<b>Details</b>', TABLE_HEADER)],
        [Paragraph('Voice', TABLE_CELL),
         Paragraph('Hoarseness, breathiness, reduced volume, voice fatigue, diplophonia', TABLE_CELL)],
        [Paragraph('Cord position', TABLE_CELL),
         Paragraph('Paramedian position (not completely abducted or adducted)', TABLE_CELL)],
        [Paragraph('Respiration', TABLE_CELL),
         Paragraph('Usually adequate (contralateral cord functions normally)', TABLE_CELL)],
        [Paragraph('Swallowing', TABLE_CELL),
         Paragraph('Mild dysphagia; aspiration of liquids in some patients', TABLE_CELL)],
        [Paragraph('If also SLN injured', TABLE_CELL),
         Paragraph('Cord lies more laterally; worse voice quality and glottic competence; '
                   'increased aspiration risk', TABLE_CELL)],
        [Paragraph('Long-term', TABLE_CELL),
         Paragraph('Contralateral cord compensates (crosses midline to touch affected cord); '
                   'voice improves over 6-12 months', TABLE_CELL)],
        [Paragraph('Treatment', TABLE_CELL),
         Paragraph('Voice therapy; vocal fold medialization (injection laryngoplasty, thyroplasty) '
                   'if no recovery; RLN reanastomosis/ansa-RLN reinnervation', TABLE_CELL)],
    ]
    uni_tbl = Table(uni_data, colWidths=[W*0.26, W*0.74])
    uni_tbl.setStyle(TableStyle([
        ('BACKGROUND', (0,0), (-1,0), MED_BLUE),
        ('ROWBACKGROUNDS', (0,1), (-1,-1), [LIGHT_BLUE, WHITE]),
        ('GRID', (0,0), (-1,-1), 0.5, HexColor('#94a3b8')),
        ('TOPPADDING', (0,0), (-1,-1), 6),
        ('BOTTOMPADDING', (0,0), (-1,-1), 6),
        ('LEFTPADDING', (0,0), (-1,-1), 7),
        ('RIGHTPADDING', (0,0), (-1,-1), 7),
        ('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
    ]))
    story += [uni_tbl, sp()]

    # Thyroid surgery incidence box
    story.append(box_para(
        'Surgical incidence: Permanent RLN paralysis ~1-2% in thyroid surgery. '
        'Temporary dysfunction 2-5% (true incidence likely higher due to neuropraxia). '
        'Higher in revision procedures, thyroid carcinoma, substernal goitre.',
        LIGHT_BLUE, MED_BLUE, text_color=DARK_BLUE, icon='📊 DATA'
    ))
    story.append(sp(2))

    # ════════════════════════════════════════════════════════════════════════════
    # SECTION 10 — Bilateral RLN Palsy
    # ════════════════════════════════════════════════════════════════════════════
    story.append(ColorBar('SECTION 10 — BILATERAL RLN PALSY', RED_DARK))
    story.append(sp())

    story.append(box_para(
        'EMERGENCY: Bilateral RLN injury = potentially life-threatening airway compromise!',
        RED_LIGHT, RED_DARK, text_color=RED_DARK, icon='🚨 EMERGENCY'
    ))
    story.append(sp())

    bi_data = [
        [Paragraph('<b>Feature</b>', TABLE_HEADER), Paragraph('<b>Details</b>', TABLE_HEADER)],
        [Paragraph('Cord position', TABLE_CELL),
         Paragraph('Both cords in median or paramedian position — no abduction possible', TABLE_CELL)],
        [Paragraph('Acute presentation', TABLE_CELL),
         Paragraph('Immediate postoperative stridor, respiratory distress, dyspnea, cyanosis', TABLE_CELL)],
        [Paragraph('Voice', TABLE_CELL),
         Paragraph('Paradoxically may be relatively normal (cords in paramedian — adequate adduction)', TABLE_CELL)],
        [Paragraph('Airway', TABLE_CELL),
         Paragraph('Severely compromised; may require immediate reintubation and tracheostomy', TABLE_CELL)],
        [Paragraph('Delayed presentation', TABLE_CELL),
         Paragraph('Cords gradually move to midline; airway progressively worsens over days/weeks', TABLE_CELL)],
        [Paragraph('Management', TABLE_CELL),
         Paragraph('Immediate: Secure airway (reintubation/tracheostomy). '
                   'Long-term: Laser arytenoidectomy, posterior cordotomy, or reinnervation procedures', TABLE_CELL)],
        [Paragraph('Ansa-RLN anastomosis', TABLE_CELL),
         Paragraph('Prevents laryngeal synkinesis and vocal cord hyperadduction; '
                   'may not restore movement but prevents atrophy', TABLE_CELL)],
    ]
    bi_tbl = Table(bi_data, colWidths=[W*0.25, W*0.75])
    bi_tbl.setStyle(TableStyle([
        ('BACKGROUND', (0,0), (-1,0), RED_DARK),
        ('ROWBACKGROUNDS', (0,1), (-1,-1), [RED_LIGHT, WHITE]),
        ('GRID', (0,0), (-1,-1), 0.5, HexColor('#94a3b8')),
        ('TOPPADDING', (0,0), (-1,-1), 6),
        ('BOTTOMPADDING', (0,0), (-1,-1), 6),
        ('LEFTPADDING', (0,0), (-1,-1), 7),
        ('RIGHTPADDING', (0,0), (-1,-1), 7),
        ('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
    ]))
    story += [bi_tbl, PageBreak()]

    # ════════════════════════════════════════════════════════════════════════════
    # SECTION 11 — High-Yield Exam Points
    # ════════════════════════════════════════════════════════════════════════════
    story.append(ColorBar('SECTION 11 — HIGH-YIELD EXAM POINTS', HexColor('#b45309')))
    story.append(sp())

    hy_data = [
        [Paragraph('<b>#</b>', TABLE_HEADER), Paragraph('<b>High-Yield Fact</b>', TABLE_HEADER)],
        [Paragraph('1', TABLE_CELL_C),
         Paragraph('The ONLY intrinsic laryngeal muscle NOT supplied by RLN is <b>Cricothyroid</b> (supplied by EBSLN)', TABLE_CELL)],
        [Paragraph('2', TABLE_CELL_C),
         Paragraph('The ONLY abductor of the vocal folds is the <b>Posterior CricoArytenoid (PCA)</b>', TABLE_CELL)],
        [Paragraph('3', TABLE_CELL_C),
         Paragraph('Non-recurrent laryngeal nerve occurs in <b>~2%</b>, almost always on the <b>RIGHT</b>, '
                   'associated with retroesophageal subclavian artery', TABLE_CELL)],
        [Paragraph('4', TABLE_CELL_C),
         Paragraph('Left RLN loops around the <b>arch of the aorta</b> at the <b>ligamentum arteriosum</b>; '
                   'right loops around the <b>subclavian artery</b>', TABLE_CELL)],
        [Paragraph('5', TABLE_CELL_C),
         Paragraph('<b>Ortner\'s syndrome (Cardiovocal syndrome):</b> Left VCP due to left RLN compression by '
                   'enlarged left atrium (mitral stenosis) or aortic aneurysm', TABLE_CELL)],
        [Paragraph('6', TABLE_CELL_C),
         Paragraph('Aortopulmonary window: space between pulmonary artery and aorta — '
                   'left RLN passes through here; a common site for lung cancer/lymph node compression', TABLE_CELL)],
        [Paragraph('7', TABLE_CELL_C),
         Paragraph('<b>Berry\'s ligament:</b> RLN is most vulnerable here — especially when ligament is low and posterior', TABLE_CELL)],
        [Paragraph('8', TABLE_CELL_C),
         Paragraph('<b>Behrs triangle</b> (lateral approach to RLN): bounded by inferior thyroid artery, trachea, and common carotid', TABLE_CELL)],
        [Paragraph('9', TABLE_CELL_C),
         Paragraph('<b>Joll\'s triangle</b> (EBSLN identification): superior thyroid pedicle + cricothyroid muscle + lower edge of thyroid cartilage', TABLE_CELL)],
        [Paragraph('10', TABLE_CELL_C),
         Paragraph('<b>Galen\'s anastomosis:</b> Between IBSLN (terminal branch) and ascending branch of RLN — PURELY SENSORY', TABLE_CELL)],
        [Paragraph('11', TABLE_CELL_C),
         Paragraph('Unilateral VCP: cord in paramedian position → hoarse, breathy voice; '
                   'Bilateral VCP: cords in median/paramedian → stridor, airway compromise', TABLE_CELL)],
        [Paragraph('12', TABLE_CELL_C),
         Paragraph('IBSLN can be used for LA injection in the piriform fossa for endoscopy/procedures', TABLE_CELL)],
        [Paragraph('13', TABLE_CELL_C),
         Paragraph('Sensory level: ABOVE vocal folds = SLN (internal branch); '
                   'BELOW vocal folds = RLN', TABLE_CELL)],
        [Paragraph('14', TABLE_CELL_C),
         Paragraph('Vagal lesion ABOVE the RLN takeoff will also paralyse the vocal cord '
                   '(+ pharyngeal, palatine, cardiac, oesophageal functions affected)', TABLE_CELL)],
        [Paragraph('15', TABLE_CELL_C),
         Paragraph('EBSLN injury in thyroid surgery: up to 25%; often unrecognized; '
                   'causes pitch problems (high-pitch loss) — important for singers (Semon\'s concept)', TABLE_CELL)],
    ]
    hy_tbl = Table(hy_data, colWidths=[0.8*cm, W - 0.8*cm])
    hy_tbl.setStyle(TableStyle([
        ('BACKGROUND', (0,0), (-1,0), HexColor('#b45309')),
        ('ROWBACKGROUNDS', (0,1), (-1,-1), [ORANGE_LIGHT, WHITE]),
        ('GRID', (0,0), (-1,-1), 0.5, HexColor('#d97706')),
        ('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), 'MIDDLE'),
        ('ALIGN', (0,0), (0,-1), 'CENTER'),
        ('FONTNAME', (0,0), (0,-1), 'Helvetica-Bold'),
    ]))
    story += [hy_tbl, sp(2)]

    # Quick comparison table — causes of VCP
    story.append(Paragraph('Causes of Vocal Cord Palsy — Approach by Level', H2))
    level_data = [
        [Paragraph('<b>Level</b>', TABLE_HEADER), Paragraph('<b>Structures</b>', TABLE_HEADER),
         Paragraph('<b>Causes of VCP</b>', TABLE_HEADER)],
        [Paragraph('Brainstem/CNS', TABLE_CELL),
         Paragraph('Vagal nucleus (nucleus ambiguus)', TABLE_CELL),
         Paragraph('Stroke, MS, tumour, syringobulbia, motor neurone disease', TABLE_CELL)],
        [Paragraph('Skull base / Jugular foramen', TABLE_CELL),
         Paragraph('CN X exits skull base', TABLE_CELL),
         Paragraph('Glomus jugulare, metastases, meningioma, skull base fracture', TABLE_CELL)],
        [Paragraph('Neck (above RLN origin)', TABLE_CELL),
         Paragraph('Vagus nerve in carotid sheath', TABLE_CELL),
         Paragraph('Carotid body tumour, carotid endarterectomy, neck dissection, thyroid surgery, trauma', TABLE_CELL)],
        [Paragraph('Thorax — Right', TABLE_CELL),
         Paragraph('RLN loops around subclavian', TABLE_CELL),
         Paragraph('Subclavian artery aneurysm, apex lung cancer, thyroid malignancy, lymphadenopathy', TABLE_CELL)],
        [Paragraph('Thorax — Left', TABLE_CELL),
         Paragraph('RLN loops around aorta', TABLE_CELL),
         Paragraph('Lung cancer (aortopulmonary window), aortic aneurysm, mitral stenosis (Ortner\'s), '
                   'mediastinal lymphadenopathy, cardiac surgery', TABLE_CELL)],
        [Paragraph('Local (larynx)', TABLE_CELL),
         Paragraph('Cricothyroid joint', TABLE_CELL),
         Paragraph('Post-intubation trauma, laryngeal carcinoma, laryngeal trauma', TABLE_CELL)],
    ]
    level_tbl = Table(level_data, colWidths=[W*0.20, W*0.27, W*0.53])
    level_tbl.setStyle(TableStyle([
        ('BACKGROUND', (0,0), (-1,0), DARK_BLUE),
        ('ROWBACKGROUNDS', (0,1), (-1,-1), [LIGHT_BLUE, WHITE]),
        ('GRID', (0,0), (-1,-1), 0.5, HexColor('#94a3b8')),
        ('TOPPADDING', (0,0), (-1,-1), 6),
        ('BOTTOMPADDING', (0,0), (-1,-1), 6),
        ('LEFTPADDING', (0,0), (-1,-1), 7),
        ('RIGHTPADDING', (0,0), (-1,-1), 7),
        ('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
    ]))
    story += [level_tbl, sp(2)]

    # Final diagram
    story.append(Paragraph('Cross-sectional Anatomy Reference', H2))
    img2 = fetch_img(
        'https://cdn.orris.care/cdss_images/394aa6d625cd88d3aa5d3b60ecd530bace74f1cf3e79731529fe89a8d6a1ca44.png',
        W * 0.6, 10*cm)
    if img2:
        img_tbl2 = Table([[img2]], colWidths=[W])
        img_tbl2.setStyle(TableStyle([
            ('ALIGN', (0,0), (-1,-1), 'CENTER'),
            ('TOPPADDING', (0,0), (-1,-1), 4),
            ('BOTTOMPADDING', (0,0), (-1,-1), 4),
        ]))
        story.append(img_tbl2)
        story.append(Paragraph(
            'Fig. 2 — Cross-section at the level of the glottis showing the relationship of structures '
            '(Cummings Otolaryngology Head and Neck Surgery)', CAPTION))

    story.append(sp())

    # References box
    refs_data = [[Paragraph(
        '<b>Sources:</b> Scott-Brown\'s Otorhinolaryngology Head &amp; Neck Surgery (Vols 1 &amp; 2) | '
        'Cummings Otolaryngology Head and Neck Surgery (6e) | Bailey &amp; Love\'s Short Practice of Surgery (28e) | '
        'Gray\'s Anatomy for Students | 22nd Edition Pye\'s Surgical Handicraft',
        s('ref', fontSize=9, textColor=DARK_BLUE, fontName='Helvetica', leading=13))]]
    refs_tbl = Table(refs_data, colWidths=[W])
    refs_tbl.setStyle(TableStyle([
        ('BACKGROUND', (0,0), (-1,-1), LIGHT_BLUE),
        ('BOX', (0,0), (-1,-1), 1, MED_BLUE),
        ('TOPPADDING', (0,0), (-1,-1), 8),
        ('BOTTOMPADDING', (0,0), (-1,-1), 8),
        ('LEFTPADDING', (0,0), (-1,-1), 10),
        ('RIGHTPADDING', (0,0), (-1,-1), 10),
    ]))
    story.append(refs_tbl)

    # Build
    doc.build(story, onFirstPage=on_page, onLaterPages=on_page)
    print(f"PDF saved to: {out_path}")


if __name__ == '__main__':
    out = '/home/daytona/workspace/laryngeal-nerves/Laryngeal_Nerves_ENT_PG.pdf'
    build_pdf(out)
Running Command

cd /home/daytona/workspace/laryngeal-nerves && python3 generate_pdf.py 2>&1

Running Command

ls -lh /home/daytona/workspace/laryngeal-nerves/Laryngeal_Nerves_ENT_PG.pdf

Your PDF is ready! Here's the download link:

Laryngeal Nerves ENT PG

PDF Document · PDF


What's Inside (1.3 MB, A4 format)

The PDF is compiled from Scott-Brown's ORL HNS, Cummings Otolaryngology, Bailey & Love, Gray's Anatomy and covers 11 structured sections:
SectionContent
1Overview - laryngeal nerve supply table
2Superior Laryngeal Nerve - origin, course, IBSLN & EBSLN, Joll's triangle
3Recurrent Laryngeal Nerve - origin, course (R vs L), branches, Behrs triangle, Berry's ligament
4Left vs Right RLN - detailed comparison table
5Non-Recurrent Laryngeal Nerve - incidence, associations, surgical risk
6Galen's Anastomosis & human communicating nerve
7Motor/sensory distribution - full intrinsic muscle innervation table + sensory levels
8SLN injury - EBSLN/IBSLN clinical features, Joll's triangle, prevention
9RLN injury - causes by side, unilateral palsy features, surgical incidence data
10Bilateral RLN palsy - emergency management
1115 High-yield exam points + VCP causes by anatomical level
Key diagrams included:
  • Nerve course diagram (from Scott-Brown's) showing the vagus, SLN with internal/external branches, and the RLN entering the larynx
  • Cross-sectional glottis anatomy (Cummings)
  • Color-coded mnemonics, clinical pearl boxes, and exam alert boxes throughout
This is a shared conversation. Sign in to Orris to start your own chat.