attachment
attachment

Viva answers to all these questions and cross questions that could be asked

Reading File
Finding Sources
All 12 questions from your paper are covered above. Here is a quick orientation to the guide:
What's included for each question:
  • A structured main answer covering all required points
  • 4-6 probable cross-questions with clean, direct answers
  • Key numbers, comparisons, and distinguishing features highlighted
High-priority viva traps to NOT miss:
  • Globulomaxillary "cyst" - always call it controversial/no longer a true fissural cyst
  • Codman's triangle - NOT pathognomonic of osteosarcoma
  • PDL widening = earliest jaw osteosarcoma sign (examiners love this)
  • SLOB rule for PDR cross-question
  • Reticular OLP = most common; Erosive = highest malignant potential
  • Speckled leukoplakia has the highest malignant transformation rate among all leukoplakia subtypes
  • Rectangular collimator reduces dose more than round - always specify the 60-70% figure
Good luck with your viva!

Create a study PDF with key figures and conversion tables for radiation topics

Reading File
Running Command

ls -la /home/daytona/workspace/ 2>/dev/null || echo "workspace empty or not foun…

Running Command

mkdir -p /home/daytona/workspace/radiation-study && python3 -c "import reportlab…

Writing File

~/radiation-study/build_pdf.py

from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.units import cm, mm
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.platypus import (
    SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
    HRFlowable, KeepTogether, PageBreak
)
from reportlab.platypus.flowables import Flowable
from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_RIGHT
from reportlab.graphics.shapes import Drawing, Rect, String, Line, Circle, Polygon
from reportlab.graphics.charts.barcharts import VerticalBarChart
from reportlab.graphics import renderPDF
import math

# ── Colour palette ──────────────────────────────────────────────────────────
NAVY      = colors.HexColor("#1a2a4a")
TEAL      = colors.HexColor("#0d7377")
GOLD      = colors.HexColor("#f5a623")
LIGHT_BG  = colors.HexColor("#eef4fb")
LIGHT_TL  = colors.HexColor("#e0f5f5")
RED_ACC   = colors.HexColor("#c0392b")
GREEN_ACC = colors.HexColor("#27ae60")
ORANGE    = colors.HexColor("#e67e22")
PURPLE    = colors.HexColor("#8e44ad")
WHITE     = colors.white
GREY_LT   = colors.HexColor("#f2f2f2")
GREY_MID  = colors.HexColor("#bdc3c7")

W, H = A4

# ── Styles ───────────────────────────────────────────────────────────────────
base = getSampleStyleSheet()

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

TITLE_S  = S("title_s",  fontName="Helvetica-Bold",   fontSize=24, textColor=WHITE,       alignment=TA_CENTER, spaceAfter=4)
SUB_S    = S("sub_s",    fontName="Helvetica",         fontSize=11, textColor=LIGHT_BG,    alignment=TA_CENTER, spaceAfter=2)
H1_S     = S("h1_s",     fontName="Helvetica-Bold",   fontSize=14, textColor=NAVY,        spaceBefore=12, spaceAfter=4)
H2_S     = S("h2_s",     fontName="Helvetica-Bold",   fontSize=11, textColor=WHITE,       spaceBefore=2, spaceAfter=2)
BODY_S   = S("body_s",   fontName="Helvetica",         fontSize=9,  textColor=colors.black, leading=14, spaceAfter=3)
SMALL_S  = S("small_s",  fontName="Helvetica",         fontSize=8,  textColor=colors.HexColor("#444444"), leading=12)
BOLD_S   = S("bold_s",   fontName="Helvetica-Bold",   fontSize=9,  textColor=NAVY)
NOTE_S   = S("note_s",   fontName="Helvetica-Oblique",fontSize=8,  textColor=RED_ACC, leading=12)
FOOT_S   = S("foot_s",   fontName="Helvetica",         fontSize=7,  textColor=GREY_MID, alignment=TA_CENTER)
CELL_HDR = S("cell_hdr", fontName="Helvetica-Bold",   fontSize=8,  textColor=WHITE,       alignment=TA_CENTER, leading=10)
CELL_S   = S("cell_s",   fontName="Helvetica",         fontSize=8,  textColor=colors.black, alignment=TA_CENTER, leading=10)
CELL_L   = S("cell_l",   fontName="Helvetica",         fontSize=8,  textColor=colors.black, alignment=TA_LEFT,   leading=10)
CELL_BL  = S("cell_bl",  fontName="Helvetica-Bold",   fontSize=8,  textColor=NAVY,        alignment=TA_LEFT,   leading=10)

# ── Helpers ───────────────────────────────────────────────────────────────────
def hdr_para(text, style=H2_S, bg=NAVY):
    """Coloured header bar with white text via single-cell table."""
    t = Table([[Paragraph(text, style)]], colWidths=[W - 4*cm])
    t.setStyle(TableStyle([
        ("BACKGROUND", (0,0), (-1,-1), bg),
        ("LEFTPADDING",  (0,0), (-1,-1), 8),
        ("RIGHTPADDING", (0,0), (-1,-1), 8),
        ("TOPPADDING",   (0,0), (-1,-1), 5),
        ("BOTTOMPADDING",(0,0), (-1,-1), 5),
        ("ROUNDEDCORNERS", (0,0), (-1,-1), [4,4,4,4]),
    ]))
    return t

def box_table(data, col_widths, header_rows=1,
              hdr_bg=NAVY, alt_bg=LIGHT_BG, border=TEAL):
    style = [
        ("BACKGROUND",   (0,0), (-1, header_rows-1), hdr_bg),
        ("TEXTCOLOR",    (0,0), (-1, header_rows-1), WHITE),
        ("FONTNAME",     (0,0), (-1, header_rows-1), "Helvetica-Bold"),
        ("FONTSIZE",     (0,0), (-1,-1), 8),
        ("ALIGN",        (0,0), (-1,-1), "CENTER"),
        ("VALIGN",       (0,0), (-1,-1), "MIDDLE"),
        ("GRID",         (0,0), (-1,-1), 0.4, border),
        ("ROWBACKGROUNDS",(0, header_rows), (-1,-1), [WHITE, alt_bg]),
        ("TOPPADDING",   (0,0), (-1,-1), 4),
        ("BOTTOMPADDING",(0,0), (-1,-1), 4),
        ("LEFTPADDING",  (0,0), (-1,-1), 5),
        ("RIGHTPADDING", (0,0), (-1,-1), 5),
    ]
    return Table(data, colWidths=col_widths, style=TableStyle(style), repeatRows=header_rows)

def bullet(text, indent=0.3):
    return Paragraph(f"<bullet>&#8226;</bullet> {text}", 
                     ParagraphStyle("bul", fontName="Helvetica", fontSize=8.5,
                                    textColor=colors.black, leading=13,
                                    leftIndent=indent*cm, spaceAfter=2))

def red_bullet(text):
    return Paragraph(f"<font color='#c0392b'><b>!</b></font>  {text}",
                     ParagraphStyle("rbul", fontName="Helvetica", fontSize=8.5,
                                    textColor=colors.black, leading=13,
                                    leftIndent=0.4*cm, spaceAfter=2))

def sp(n=1): return Spacer(1, n*0.35*cm)

# ── Custom Flowables ──────────────────────────────────────────────────────────

class TitleBlock(Flowable):
    """Full-width gradient-style title block."""
    def __init__(self, w=W-4*cm, h=3.8*cm):
        super().__init__()
        self.W = w; self.H = h
    def wrap(self, *args): return self.W, self.H
    def draw(self):
        c = self.canv
        # Background gradient (simulate with rects)
        steps = 30
        for i in range(steps):
            r = NAVY.red   + (TEAL.red   - NAVY.red)   * i / steps
            g = NAVY.green + (TEAL.green - NAVY.green)  * i / steps
            b = NAVY.blue  + (TEAL.blue  - NAVY.blue)   * i / steps
            c.setFillColorRGB(r, g, b)
            c.rect(self.W*i/steps, 0, self.W/steps+1, self.H, fill=1, stroke=0)
        # Gold accent bar
        c.setFillColor(GOLD)
        c.rect(0, self.H-0.18*cm, self.W, 0.18*cm, fill=1, stroke=0)
        c.rect(0, 0, self.W, 0.12*cm, fill=1, stroke=0)
        # Title text
        c.setFillColor(WHITE)
        c.setFont("Helvetica-Bold", 22)
        c.drawCentredString(self.W/2, self.H - 1.2*cm, "ORAL RADIOLOGY")
        c.setFont("Helvetica-Bold", 17)
        c.drawCentredString(self.W/2, self.H - 2.0*cm, "Radiation Study Guide")
        c.setFont("Helvetica", 10)
        c.setFillColor(LIGHT_BG)
        c.drawCentredString(self.W/2, self.H - 2.65*cm,
                            "Key Figures · Conversion Tables · Clinical Thresholds · Exam Mnemonics")
        c.setFont("Helvetica-Oblique", 8)
        c.setFillColor(GOLD)
        c.drawCentredString(self.W/2, self.H - 3.15*cm, "Oral Medicine & Oral Radiology  |  BDS / MDS Viva Preparation")


class XRayTubeDiagram(Flowable):
    """Schematic of X-ray tube components."""
    def __init__(self, w=16*cm, h=7.5*cm):
        super().__init__()
        self.W = w; self.H = h
    def wrap(self, *args): return self.W, self.H
    def draw(self):
        c = self.canv
        W, H = self.W, self.H
        # Glass envelope
        c.setStrokeColor(TEAL); c.setLineWidth(1.5)
        c.setFillColor(colors.HexColor("#e8f8f8"))
        c.roundRect(0.3*cm, 0.8*cm, W-0.6*cm, H-1.4*cm, 0.5*cm, fill=1, stroke=1)
        # Label
        c.setFont("Helvetica-Bold", 8); c.setFillColor(TEAL)
        c.drawString(0.5*cm, H-0.55*cm, "Glass/Metal Envelope (Vacuum)")

        # Cathode assembly
        cx = 1.8*cm
        c.setFillColor(NAVY); c.setStrokeColor(NAVY); c.setLineWidth(1)
        c.rect(cx-0.35*cm, H/2-1.1*cm, 0.7*cm, 2.2*cm, fill=1, stroke=0)
        # Filament coil symbol
        c.setStrokeColor(GOLD); c.setLineWidth(2); c.setFillColor(GOLD)
        for i in range(5):
            y = H/2 - 0.6*cm + i*0.22*cm
            c.arc(cx-0.12*cm, y, cx+0.12*cm, y+0.2*cm, 0, 180)
        c.setFont("Helvetica-Bold", 8); c.setFillColor(NAVY)
        c.drawCentredString(cx, 0.45*cm, "CATHODE")
        c.setFont("Helvetica", 7); c.setFillColor(colors.black)
        c.drawCentredString(cx, 0.22*cm, "(Tungsten filament)")

        # Electron beam arrows
        ax_start = cx + 0.4*cm
        ax_end   = W - 3.2*cm
        c.setStrokeColor(GOLD); c.setLineWidth(1.2)
        for dy in [-0.25*cm, 0, 0.25*cm]:
            c.line(ax_start, H/2+dy, ax_end, H/2+dy)
            # arrowhead
            c.setFillColor(GOLD)
            c.polygon([ax_end, H/2+dy,
                       ax_end-0.22*cm, H/2+dy+0.1*cm,
                       ax_end-0.22*cm, H/2+dy-0.1*cm], fill=1)
        c.setFont("Helvetica-Oblique", 7); c.setFillColor(GOLD)
        c.drawCentredString((ax_start+ax_end)/2, H/2+0.45*cm, "electron beam")

        # Anode
        atx = W - 2.5*cm
        c.setFillColor(colors.HexColor("#b7950b"))
        c.polygon([atx, H/2-0.7*cm,
                   atx+0.9*cm, H/2,
                   atx, H/2+0.7*cm], fill=1, stroke=0)
        c.setFont("Helvetica-Bold", 8); c.setFillColor(NAVY)
        c.drawCentredString(atx+0.35*cm, 0.45*cm, "ANODE")
        c.setFont("Helvetica", 7); c.setFillColor(colors.black)
        c.drawCentredString(atx+0.35*cm, 0.22*cm, "(Tungsten target)")

        # X-ray beam downward
        bx = atx + 0.05*cm
        c.setStrokeColor(RED_ACC); c.setLineWidth(2)
        c.line(bx, H/2-0.7*cm, bx, 0.95*cm)
        # diverging rays
        c.setLineWidth(1)
        for angle in [-25, 0, 25]:
            rad = math.radians(270 + angle)
            ex = bx + 1.4*cm * math.cos(rad)
            ey = H/2-0.7*cm + 1.4*cm * math.sin(rad)
            c.line(bx, H/2-0.7*cm, ex, ey)
        c.setFont("Helvetica-Bold", 7.5); c.setFillColor(RED_ACC)
        c.drawCentredString(bx, 0.7*cm, "X-RAY BEAM")

        # Focusing cup label
        c.setFont("Helvetica", 7); c.setFillColor(PURPLE)
        c.drawString(0.1*cm, H/2+1.0*cm, "Focusing cup")
        c.setStrokeColor(PURPLE); c.setLineWidth(0.5)
        c.line(1.15*cm, H/2+0.9*cm, cx-0.35*cm, H/2+0.7*cm)

        # kV label
        c.setFont("Helvetica-Bold", 8); c.setFillColor(TEAL)
        c.drawCentredString(W/2, H-0.5*cm, "High Voltage (kVp 60-120)")
        c.setStrokeColor(TEAL); c.setLineWidth(0.8)
        c.line(1.5*cm, H-0.35*cm, W-1.5*cm, H-0.35*cm)


class InverseSquareDiagram(Flowable):
    """Visual of inverse square law with circles."""
    def __init__(self, w=13*cm, h=5.5*cm):
        super().__init__()
        self.W = w; self.H = h
    def wrap(self, *args): return self.W, self.H
    def draw(self):
        c = self.canv
        W, H = self.W, self.H
        ox, oy = 1.0*cm, H/2
        # source
        c.setFillColor(GOLD); c.setStrokeColor(GOLD)
        c.circle(ox, oy, 0.2*cm, fill=1)
        c.setFont("Helvetica-Bold", 7.5); c.setFillColor(NAVY)
        c.drawCentredString(ox, oy-0.55*cm, "Source")

        distances = [2.5, 5.5, 9.5]
        labels    = ["d", "2d", "3d"]
        intensities = ["I", "I/4", "I/9"]
        dot_counts  = [9, 4, 2]
        dot_color   = RED_ACC

        for idx, (dx, lbl, ints, ndots) in enumerate(
                zip(distances, labels, intensities, dot_counts)):
            xpos = ox + dx*cm
            # vertical line
            c.setStrokeColor(GREY_MID); c.setLineWidth(0.7)
            c.line(xpos, oy-1.3*cm, xpos, oy+1.3*cm)
            # dots representing intensity
            c.setFillColor(dot_color)
            cols = int(math.ceil(math.sqrt(ndots)))
            for d in range(ndots):
                row = d // cols; col = d % cols
                px = xpos - 0.25*cm + col*0.18*cm
                py = oy + 0.4*cm - row*0.22*cm
                c.circle(px, py, 0.055*cm, fill=1, stroke=0)
            # labels
            c.setFont("Helvetica-Bold", 8.5); c.setFillColor(TEAL)
            c.drawCentredString(xpos, oy-1.55*cm, lbl)
            c.setFont("Helvetica", 8); c.setFillColor(NAVY)
            c.drawCentredString(xpos, oy-1.9*cm, ints)

        # rays from source
        c.setStrokeColor(GOLD); c.setLineWidth(0.6)
        for angle in [-30, -15, 0, 15, 30]:
            rad = math.radians(angle)
            c.line(ox+0.2*cm, oy,
                   ox + 10.5*cm*math.cos(rad),
                   oy + 10.5*cm*math.sin(rad))

        # formula
        c.setFont("Helvetica-Bold", 10); c.setFillColor(NAVY)
        c.drawString(0.1*cm, 0.15*cm, "I  \u221d  1 / d\u00b2")
        c.setFont("Helvetica", 7.5); c.setFillColor(colors.black)
        c.drawString(2.5*cm, 0.15*cm, "   Doubling distance \u2192 intensity reduced to 1/4")


class BeamQualityBar(Flowable):
    """Horizontal bar showing kVp effect on beam quality."""
    def __init__(self, w=15*cm, h=2.6*cm):
        super().__init__()
        self.W = w; self.H = h
    def wrap(self, *args): return self.W, self.H
    def draw(self):
        c = self.canv
        W, H = self.W, self.H
        # gradient bar
        steps = 60
        for i in range(steps):
            t = i/steps
            r = colors.HexColor("#3498db").red   * (1-t) + RED_ACC.red   * t
            g = colors.HexColor("#3498db").green * (1-t) + RED_ACC.green * t
            b = colors.HexColor("#3498db").blue  * (1-t) + RED_ACC.blue  * t
            c.setFillColorRGB(r, g, b)
            c.rect(W*i/steps, H/2-0.3*cm, W/steps+1, 0.6*cm, fill=1, stroke=0)
        # border
        c.setStrokeColor(NAVY); c.setLineWidth(1)
        c.rect(0, H/2-0.3*cm, W, 0.6*cm, fill=0, stroke=1)
        # ticks
        kvps = [50, 60, 65, 70, 80, 90, 100, 120]
        for kv in kvps:
            x = W * (kv-50)/(120-50)
            c.setStrokeColor(WHITE); c.setLineWidth(0.8)
            c.line(x, H/2-0.28*cm, x, H/2+0.28*cm)
            c.setFont("Helvetica", 6.5); c.setFillColor(NAVY)
            c.drawCentredString(x, H/2-0.55*cm, str(kv))
        c.setFont("Helvetica-Bold", 7.5); c.setFillColor(NAVY)
        c.drawString(0, 0.1*cm, "50 kVp  (Soft / Low energy)")
        c.drawRightString(W, 0.1*cm, "120 kVp  (Hard / High energy)")
        c.setFont("Helvetica-Bold", 8); c.setFillColor(WHITE)
        c.drawCentredString(W/2, H/2-0.12*cm, "kVp (kilovoltage peak)")
        # filtration markers
        c.setStrokeColor(GOLD); c.setLineWidth(1.5)
        x70 = W * (70-50)/(120-50)
        c.line(x70, H/2+0.3*cm, x70, H/2+0.7*cm)
        c.setFont("Helvetica-Bold", 7); c.setFillColor(GOLD)
        c.drawCentredString(x70, H/2+0.78*cm, "70 kVp threshold")
        c.setFont("Helvetica", 6.5); c.setFillColor(TEAL)
        c.drawCentredString(W*0.15, H-0.1*cm, "Filter: 1.5 mm Al")
        c.drawCentredString(W*0.7,  H-0.1*cm, "Filter: 2.5 mm Al")


class RadiationScaleDiagram(Flowable):
    """Electromagnetic spectrum snippet showing X-ray position."""
    def __init__(self, w=15*cm, h=3.2*cm):
        super().__init__()
        self.W = w; self.H = h
    def wrap(self, *args): return self.W, self.H
    def draw(self):
        c = self.canv
        W, H = self.W, self.H
        segments = [
            ("Radio", colors.HexColor("#2980b9"), "10³m"),
            ("Micro", colors.HexColor("#27ae60"), "1mm"),
            ("IR",    colors.HexColor("#e67e22"), "700nm"),
            ("Visible",colors.HexColor("#f1c40f"),"400nm"),
            ("UV",    PURPLE,                     "10nm"),
            ("X-RAY", RED_ACC,                    "0.01nm"),
            ("Gamma", colors.HexColor("#922b21"), "0.001nm"),
        ]
        sw = W / len(segments)
        for i, (label, col, wl) in enumerate(segments):
            c.setFillColor(col)
            c.rect(i*sw, H/2, sw-1, H/2-0.1*cm, fill=1, stroke=0)
            c.setFillColor(WHITE if col != colors.HexColor("#f1c40f") else NAVY)
            c.setFont("Helvetica-Bold", 7)
            c.drawCentredString(i*sw + sw/2, H/2+0.5*cm, label)
            c.setFont("Helvetica", 6)
            c.setFillColor(NAVY)
            c.drawCentredString(i*sw + sw/2, H/2+0.22*cm, wl)
        # Arrow: increasing energy
        c.setStrokeColor(NAVY); c.setFillColor(NAVY); c.setLineWidth(1)
        c.line(0.1*cm, H/2-0.35*cm, W-0.1*cm, H/2-0.35*cm)
        c.polygon([W-0.1*cm, H/2-0.35*cm,
                   W-0.4*cm, H/2-0.22*cm,
                   W-0.4*cm, H/2-0.48*cm], fill=1)
        c.setFont("Helvetica-Bold", 7.5); c.setFillColor(NAVY)
        c.drawString(0.1*cm, H/2-0.6*cm, "Increasing Energy / Frequency")
        c.drawRightString(W, H/2-0.6*cm, "Decreasing Wavelength")
        # X-ray highlight box
        xi = 5 * sw
        c.setStrokeColor(GOLD); c.setLineWidth(2)
        c.rect(xi, H/2, sw-1, H/2-0.1*cm, fill=0, stroke=1)


# ── PAGE HEADER / FOOTER ──────────────────────────────────────────────────────
def on_page(canvas, doc):
    canvas.saveState()
    # Top bar
    canvas.setFillColor(NAVY)
    canvas.rect(0, H-0.65*cm, W, 0.65*cm, fill=1, stroke=0)
    canvas.setFillColor(GOLD)
    canvas.setFont("Helvetica-Bold", 8)
    canvas.drawString(1.5*cm, H-0.45*cm, "ORAL RADIOLOGY  |  Radiation Study Guide")
    canvas.setFillColor(LIGHT_BG)
    canvas.setFont("Helvetica", 8)
    canvas.drawRightString(W-1.5*cm, H-0.45*cm, f"Page {doc.page}")
    # Bottom bar
    canvas.setFillColor(TEAL)
    canvas.rect(0, 0, W, 0.4*cm, fill=1, stroke=0)
    canvas.setFillColor(WHITE)
    canvas.setFont("Helvetica", 6.5)
    canvas.drawCentredString(W/2, 0.12*cm, "BDS/MDS Viva Preparation  |  Oral Medicine & Oral Radiology")
    canvas.restoreState()

def on_first_page(canvas, doc):
    canvas.saveState()
    canvas.setFillColor(TEAL)
    canvas.rect(0, 0, W, 0.4*cm, fill=1, stroke=0)
    canvas.setFillColor(WHITE); canvas.setFont("Helvetica", 6.5)
    canvas.drawCentredString(W/2, 0.12*cm, "BDS/MDS Viva Preparation  |  Oral Medicine & Oral Radiology")
    canvas.restoreState()


# ── BUILD ─────────────────────────────────────────────────────────────────────
def build():
    path = "/home/daytona/workspace/radiation-study/Radiation_Study_Guide.pdf"
    doc = SimpleDocTemplate(
        path, pagesize=A4,
        leftMargin=2*cm, rightMargin=2*cm,
        topMargin=1.2*cm, bottomMargin=1.2*cm,
        title="Oral Radiology Radiation Study Guide",
        author="Oral Radiology Viva Prep"
    )
    story = []
    CW = W - 4*cm  # content width

    # ═══════════════════════════════════════════════════════════════════════
    # TITLE BLOCK
    # ═══════════════════════════════════════════════════════════════════════
    story.append(TitleBlock(CW, 3.8*cm))
    story.append(sp(1.5))

    # Quick nav pills (simulated as coloured table cells)
    nav_data = [["X-RAY TUBE", "RADIATION UNITS", "FILTRATION", "DOSE LIMITS", "INVERSE SQ.", "EM SPECTRUM"]]
    nav_cols = [CW/6]*6
    nav_t = Table(nav_data, colWidths=nav_cols)
    nav_t.setStyle(TableStyle([
        ("BACKGROUND",  (0,0),(0,0), TEAL),
        ("BACKGROUND",  (1,0),(1,0), NAVY),
        ("BACKGROUND",  (2,0),(2,0), TEAL),
        ("BACKGROUND",  (3,0),(3,0), RED_ACC),
        ("BACKGROUND",  (4,0),(4,0), NAVY),
        ("BACKGROUND",  (5,0),(5,0), TEAL),
        ("TEXTCOLOR",   (0,0),(-1,-1), WHITE),
        ("FONTNAME",    (0,0),(-1,-1), "Helvetica-Bold"),
        ("FONTSIZE",    (0,0),(-1,-1), 7),
        ("ALIGN",       (0,0),(-1,-1), "CENTER"),
        ("TOPPADDING",  (0,0),(-1,-1), 4),
        ("BOTTOMPADDING",(0,0),(-1,-1), 4),
    ]))
    story.append(nav_t)
    story.append(sp(1.5))

    # ═══════════════════════════════════════════════════════════════════════
    # SECTION 1 – X-RAY TUBE DIAGRAM
    # ═══════════════════════════════════════════════════════════════════════
    story.append(hdr_para("  1.  X-RAY TUBE — COMPONENTS & LABELLED DIAGRAM", bg=NAVY))
    story.append(sp())
    story.append(XRayTubeDiagram(CW, 7.5*cm))
    story.append(sp())

    # Components table
    comp_data = [
        [Paragraph("Component", CELL_HDR), Paragraph("Material", CELL_HDR),
         Paragraph("Function", CELL_HDR), Paragraph("Key Fact", CELL_HDR)],
        [Paragraph("Filament", CELL_BL), Paragraph("Tungsten wire", CELL_S),
         Paragraph("Thermionic emission — produces electrons when heated to ~2200°C", CELL_L),
         Paragraph("Heated by step-down transformer (3-5 V)", CELL_L)],
        [Paragraph("Focusing cup", CELL_BL), Paragraph("Molybdenum", CELL_S),
         Paragraph("Negative charge concentrates electron beam onto focal spot", CELL_L),
         Paragraph("Reduces focal spot size → sharper image", CELL_L)],
        [Paragraph("Anode (target)", CELL_BL), Paragraph("Tungsten (Z=74)", CELL_S),
         Paragraph("Electrons strike target → X-rays produced (Bremsstrahlung + characteristic)", CELL_L),
         Paragraph("High Z + high melting point (3422°C)", CELL_L)],
        [Paragraph("Glass/metal envelope", CELL_BL), Paragraph("Borosilicate / metal", CELL_S),
         Paragraph("Maintains vacuum; prevents electron scattering", CELL_L),
         Paragraph("~10⁻⁶ mmHg vacuum", CELL_L)],
        [Paragraph("Step-up transformer", CELL_BL), Paragraph("Copper coils", CELL_S),
         Paragraph("Increases voltage to 60-120 kVp for X-ray production", CELL_L),
         Paragraph("Controls quality (kVp)", CELL_L)],
        [Paragraph("Step-down transformer", CELL_BL), Paragraph("Copper coils", CELL_S),
         Paragraph("Reduces voltage to 3-5 V to heat the filament", CELL_L),
         Paragraph("Controls quantity (mA)", CELL_L)],
        [Paragraph("Insulating oil", CELL_BL), Paragraph("Transformer oil", CELL_S),
         Paragraph("Heat dissipation + electrical insulation", CELL_L),
         Paragraph("Surrounds X-ray tube inside tubehead", CELL_L)],
        [Paragraph("Al filter", CELL_BL), Paragraph("Aluminum (Al)", CELL_S),
         Paragraph("Removes low-energy (long λ) X-rays that irradiate patient but add no image info", CELL_L),
         Paragraph("1.5 mm (<70kVp) / 2.5 mm (≥70kVp)", CELL_L)],
        [Paragraph("Collimator", CELL_BL), Paragraph("Lead", CELL_S),
         Paragraph("Restricts beam size to area of interest; reduces patient dose and scatter", CELL_L),
         Paragraph("Round ≤7cm; Rect saves 60-70% dose", CELL_L)],
    ]
    comp_t = box_table(comp_data,
                       col_widths=[2.5*cm, 2.3*cm, 6.0*cm, 4.0*cm],
                       hdr_bg=TEAL)
    story.append(comp_t)
    story.append(sp())

    # ═══════════════════════════════════════════════════════════════════════
    # SECTION 2 – X-RAY PRODUCTION
    # ═══════════════════════════════════════════════════════════════════════
    story.append(sp(0.5))
    story.append(hdr_para("  2.  X-RAY PRODUCTION — BREMSSTRAHLUNG vs CHARACTERISTIC", bg=TEAL))
    story.append(sp())

    prod_data = [
        [Paragraph("Feature", CELL_HDR), Paragraph("Bremsstrahlung (Braking Radiation)", CELL_HDR),
         Paragraph("Characteristic Radiation", CELL_HDR)],
        [Paragraph("Mechanism", CELL_BL),
         Paragraph("Electrons decelerate near nucleus; kinetic energy → X-ray photon", CELL_L),
         Paragraph("Electrons eject inner-shell electrons; outer shell fills vacancy → photon", CELL_L)],
        [Paragraph("Spectrum", CELL_BL), Paragraph("Continuous spectrum", CELL_S),
         Paragraph("Line spectrum (discrete energies)", CELL_S)],
        [Paragraph("% of X-rays", CELL_BL), Paragraph("~99% of dental X-rays", CELL_S),
         Paragraph("~1% (only when kVp exceeds binding energy)", CELL_S)],
        [Paragraph("Energy range", CELL_BL), Paragraph("0 → max (kVp)", CELL_S),
         Paragraph("Fixed energies specific to target element", CELL_S)],
        [Paragraph("Dependency", CELL_BL), Paragraph("Depends on kVp and Z of target", CELL_S),
         Paragraph("Depends on target element (Z)", CELL_S)],
        [Paragraph("Tungsten K-edge", CELL_BL), Paragraph("N/A", CELL_S),
         Paragraph("Kα = 59.3 keV; Kβ = 67.2 keV (for W)", CELL_S)],
    ]
    story.append(box_table(prod_data, [2.5*cm, 5.5*cm, 5.5*cm], hdr_bg=NAVY))
    story.append(sp(1.5))

    # ═══════════════════════════════════════════════════════════════════════
    # SECTION 3 – RADIATION UNITS CONVERSION TABLE
    # ═══════════════════════════════════════════════════════════════════════
    story.append(PageBreak())
    story.append(hdr_para("  3.  RADIATION UNITS — COMPLETE CONVERSION TABLE", bg=NAVY))
    story.append(sp())

    units_data = [
        [Paragraph("Quantity", CELL_HDR), Paragraph("Old Unit", CELL_HDR),
         Paragraph("SI Unit", CELL_HDR), Paragraph("Conversion", CELL_HDR),
         Paragraph("Definition / Notes", CELL_HDR)],
        # Exposure
        [Paragraph("EXPOSURE", ParagraphStyle("cat", fontName="Helvetica-Bold", fontSize=8,
                    textColor=WHITE, alignment=TA_LEFT, leading=10)),
         Paragraph("Roentgen (R)", CELL_S), Paragraph("Coulomb/kg (C/kg)", CELL_S),
         Paragraph("1 R = 2.58 × 10⁻⁴ C/kg", CELL_L),
         Paragraph("Ionization of air; only for X-rays and gamma rays in air", CELL_L)],
        # Absorbed dose
        [Paragraph("ABSORBED DOSE", ParagraphStyle("cat", fontName="Helvetica-Bold", fontSize=8,
                    textColor=WHITE, alignment=TA_LEFT, leading=10)),
         Paragraph("rad", CELL_S), Paragraph("Gray (Gy)", CELL_S),
         Paragraph("1 Gy = 100 rad\n1 rad = 0.01 Gy\n1 mGy = 100 mrad", CELL_L),
         Paragraph("Energy absorbed per unit mass (J/kg); applies to any radiation in any material", CELL_L)],
        # Equivalent dose
        [Paragraph("EQUIVALENT DOSE", ParagraphStyle("cat", fontName="Helvetica-Bold", fontSize=8,
                    textColor=WHITE, alignment=TA_LEFT, leading=10)),
         Paragraph("rem", CELL_S), Paragraph("Sievert (Sv)", CELL_S),
         Paragraph("1 Sv = 100 rem\n1 rem = 0.01 Sv\n1 mSv = 100 mrem", CELL_L),
         Paragraph("Absorbed dose × radiation weighting factor (Wr); accounts for biological effectiveness", CELL_L)],
        # Effective dose
        [Paragraph("EFFECTIVE DOSE", ParagraphStyle("cat", fontName="Helvetica-Bold", fontSize=8,
                    textColor=WHITE, alignment=TA_LEFT, leading=10)),
         Paragraph("rem", CELL_S), Paragraph("Sievert (Sv)", CELL_S),
         Paragraph("Same unit as equivalent dose", CELL_L),
         Paragraph("Equivalent dose × tissue weighting factor (Wt); whole-body risk estimate", CELL_L)],
        # Activity
        [Paragraph("ACTIVITY", ParagraphStyle("cat", fontName="Helvetica-Bold", fontSize=8,
                    textColor=WHITE, alignment=TA_LEFT, leading=10)),
         Paragraph("Curie (Ci)", CELL_S), Paragraph("Becquerel (Bq)", CELL_S),
         Paragraph("1 Ci = 3.7×10¹⁰ Bq\n1 Bq = 2.7×10⁻¹¹ Ci", CELL_L),
         Paragraph("Rate of radioactive decay; 1 Bq = 1 disintegration/sec", CELL_L)],
    ]
    # colour alternating rows by category
    units_t = Table(units_data, colWidths=[2.6*cm, 2.0*cm, 2.2*cm, 3.5*cm, 4.5*cm])
    cat_bg  = NAVY
    units_t.setStyle(TableStyle([
        ("BACKGROUND",    (0,0), (-1,0), TEAL),
        ("TEXTCOLOR",     (0,0), (-1,0), WHITE),
        ("BACKGROUND",    (0,1), (-1,1), NAVY),
        ("BACKGROUND",    (0,2), (-1,2), TEAL),
        ("BACKGROUND",    (0,3), (-1,3), NAVY),
        ("BACKGROUND",    (0,4), (-1,4), TEAL),
        ("BACKGROUND",    (0,5), (-1,5), NAVY),
        ("TEXTCOLOR",     (0,1), (0,-1), WHITE),
        ("GRID",          (0,0), (-1,-1), 0.4, colors.HexColor("#aaaaaa")),
        ("FONTSIZE",      (0,0), (-1,-1), 8),
        ("TOPPADDING",    (0,0), (-1,-1), 5),
        ("BOTTOMPADDING", (0,0), (-1,-1), 5),
        ("LEFTPADDING",   (0,0), (-1,-1), 5),
        ("VALIGN",        (0,0), (-1,-1), "MIDDLE"),
        ("ROWBACKGROUNDS",(0,0), (-1,-1), [TEAL, NAVY]),
        ("TEXTCOLOR",     (1,1), (-1,-1), WHITE),
    ]))
    story.append(units_t)
    story.append(sp())

    # Radiation Weighting Factors
    story.append(hdr_para("  3a.  RADIATION WEIGHTING FACTORS (Wr) — ICRP 2007", bg=TEAL))
    story.append(sp())
    wr_data = [
        [Paragraph("Radiation Type", CELL_HDR), Paragraph("Weighting Factor (Wr)", CELL_HDR),
         Paragraph("Notes", CELL_HDR)],
        ["X-rays, Gamma rays, Beta", Paragraph("1", CELL_S), "Least damaging per unit absorbed dose"],
        ["Protons (>2 MeV)", Paragraph("2", CELL_S), "Moderate biological effectiveness"],
        ["Neutrons < 1 MeV", Paragraph("2.5 – 20", CELL_S), "Depends on energy; most variable"],
        ["Neutrons 1–50 MeV", Paragraph("10 – 20", CELL_S), "High energy neutrons most damaging"],
        ["Alpha particles, fission fragments", Paragraph("20", CELL_S), "Most biologically damaging (short range, dense ionization)"],
    ]
    story.append(box_table(wr_data, [4.5*cm, 3.5*cm, 6.8*cm], hdr_bg=NAVY))
    story.append(sp())

    # Tissue Weighting Factors
    story.append(hdr_para("  3b.  TISSUE WEIGHTING FACTORS (Wt) — ICRP 2007", bg=TEAL))
    story.append(sp())
    wt_data = [
        [Paragraph("Wt", CELL_HDR), Paragraph("Tissues/Organs", CELL_HDR)],
        ["0.12 (each)", "Bone marrow (red), Colon, Lung, Stomach, Breast, Remainder tissues"],
        ["0.08 (each)", "Gonads"],
        ["0.04 (each)", "Urinary bladder, Oesophagus, Liver, Thyroid"],
        ["0.01 (each)", "Bone surface, Brain, Salivary glands, Skin"],
        ["SUM = 1.00", "Total body weighting factor"],
    ]
    story.append(box_table(wt_data, [3*cm, 11.8*cm], hdr_bg=NAVY))
    story.append(sp(0.5))
    story.append(Paragraph("<font color='#c0392b'><b>Dental relevance:</b></font>  Thyroid Wt = 0.04; Bone marrow Wt = 0.12; Salivary glands Wt = 0.01. "
                           "Use thyroid shield + lead apron to protect these tissues.", BODY_S))
    story.append(sp(1.5))

    # ═══════════════════════════════════════════════════════════════════════
    # SECTION 4 – DOSE LIMITS
    # ═══════════════════════════════════════════════════════════════════════
    story.append(PageBreak())
    story.append(hdr_para("  4.  RADIATION DOSE LIMITS (ICRP 2007 / AERB India)", bg=NAVY))
    story.append(sp())

    dose_data = [
        [Paragraph("Category", CELL_HDR), Paragraph("Effective Dose Limit", CELL_HDR),
         Paragraph("Equivalent Dose – Lens of Eye", CELL_HDR),
         Paragraph("Equivalent Dose – Skin / Extremities", CELL_HDR)],
        [Paragraph("Radiation workers\n(occupational)", CELL_BL),
         Paragraph("20 mSv/year (averaged over 5 years)\nMax 50 mSv in any single year", CELL_S),
         Paragraph("20 mSv/year\n(ICRP 2011 update)", CELL_S),
         Paragraph("500 mSv/year", CELL_S)],
        [Paragraph("Pregnant radiation worker", CELL_BL),
         Paragraph("1 mSv for remainder of pregnancy\n(abdomen surface)", CELL_S),
         Paragraph("—", CELL_S), Paragraph("—", CELL_S)],
        [Paragraph("General public", CELL_BL),
         Paragraph("1 mSv/year", CELL_S),
         Paragraph("15 mSv/year\n(old ICRP 60 = 150 mSv)", CELL_S),
         Paragraph("50 mSv/year", CELL_S)],
        [Paragraph("Students (<18 yrs)", CELL_BL),
         Paragraph("6 mSv/year (if using radiation)", CELL_S),
         Paragraph("15 mSv/year", CELL_S),
         Paragraph("50 mSv/year", CELL_S)],
    ]
    story.append(box_table(dose_data, [3.2*cm, 4.5*cm, 3.5*cm, 3.6*cm], hdr_bg=RED_ACC))
    story.append(sp())

    # Typical dental doses table
    story.append(hdr_para("  4a.  TYPICAL EFFECTIVE DOSES — DENTAL RADIOGRAPHY", bg=TEAL))
    story.append(sp())
    dental_dose_data = [
        [Paragraph("Radiograph", CELL_HDR), Paragraph("Approximate Effective Dose", CELL_HDR),
         Paragraph("Equivalent Background Radiation", CELL_HDR)],
        ["Periapical (D-speed film)", "~8 µSv", "~1 day natural background"],
        ["Periapical (F-speed / digital)", "~1-2 µSv", "<1 day"],
        ["Bitewing (digital)", "~5 µSv", "~17 hours"],
        ["Full mouth series (18 films, rect. collimation)", "~35 µSv", "~4 days"],
        ["Panoramic (OPG)", "~14-24 µSv", "~2-3 days"],
        ["Lateral cephalogram", "~5-6 µSv", "~20 hours"],
        ["CBCT (small FOV)", "~40-100 µSv", "~1-2 weeks"],
        ["CBCT (large FOV)", "~100-600 µSv", "~2-8 weeks"],
        ["Chest X-ray (for comparison)", "~20 µSv", "~3 days"],
        ["Annual natural background (India avg.)", "~2400 µSv (2.4 mSv)", "365 days"],
    ]
    story.append(box_table(dental_dose_data, [4.5*cm, 4.5*cm, 5.8*cm], hdr_bg=NAVY))
    story.append(sp(1.5))

    # ═══════════════════════════════════════════════════════════════════════
    # SECTION 5 – FILTRATION TABLE
    # ═══════════════════════════════════════════════════════════════════════
    story.append(hdr_para("  5.  FILTRATION — COMPLETE REFERENCE TABLE", bg=NAVY))
    story.append(sp())
    story.append(BeamQualityBar(CW, 2.6*cm))
    story.append(sp())

    filt_data = [
        [Paragraph("Type", CELL_HDR), Paragraph("Source", CELL_HDR),
         Paragraph("Composition", CELL_HDR), Paragraph("Amount", CELL_HDR), Paragraph("Notes", CELL_HDR)],
        ["Inherent filtration", "Built into tube", "Glass envelope + oil + tubehead seal",
         "~0.5–1.0 mm Al equiv.", "Cannot be altered; fixed"],
        ["Added filtration", "External disk at port", "Aluminum (most common)\nCopper (high kVp units)",
         "Variable", "Can be changed; added by manufacturer/operator"],
        ["Total filtration", "Inherent + Added", "Combined", 
         "≥1.5 mm Al (<70 kVp)\n≥2.5 mm Al (≥70 kVp)",
         "NCRP / BDA / AERB requirement"],
        ["Thoraeus filter", "Therapeutic units", "Tin (Sn) + Cu + Al",
         "Used in radiotherapy", "K-edge filter; hardens beam maximally"],
        ["Compensating filter", "Panoramic / Ceph.", "Wedge/step shape Al",
         "Variable", "Equalises beam for different tissue thicknesses"],
    ]
    story.append(box_table(filt_data, [2.8*cm, 2.5*cm, 3.2*cm, 3.0*cm, 3.3*cm], hdr_bg=TEAL))
    story.append(sp())

    # Collimation comparison
    story.append(hdr_para("  5a.  COLLIMATION TYPES — COMPARISON", bg=TEAL))
    story.append(sp())
    col_data = [
        [Paragraph("Type", CELL_HDR), Paragraph("Shape", CELL_HDR),
         Paragraph("Beam Size at Skin", CELL_HDR), Paragraph("Dose Reduction", CELL_HDR),
         Paragraph("Scatter Reduction", CELL_HDR), Paragraph("Use", CELL_HDR)],
        ["Round / Circular", "Circle", "≤7 cm diameter", "Baseline", "Moderate", "Intraoral (routine)"],
        ["Rectangular", "Rectangle (~3×4 cm)", "Matches film size", "60–70% vs round", "Maximum", "BEST practice"],
        ["Cylindrical cone", "Open cylinder", "Round (variable)", "Moderate", "Good", "Beam indicator / PID"],
        ["Lead diaphragm", "Adjustable aperture", "Variable", "Adjustable", "Adjustable", "Medical units"],
    ]
    story.append(box_table(col_data, [2.5*cm, 2.5*cm, 2.8*cm, 2.5*cm, 2.3*cm, 2.2*cm], hdr_bg=NAVY))
    story.append(sp(1.5))

    # ═══════════════════════════════════════════════════════════════════════
    # SECTION 6 – INVERSE SQUARE LAW
    # ═══════════════════════════════════════════════════════════════════════
    story.append(PageBreak())
    story.append(hdr_para("  6.  INVERSE SQUARE LAW — FORMULA, DIAGRAM & WORKED EXAMPLES", bg=NAVY))
    story.append(sp())
    story.append(InverseSquareDiagram(CW, 5.5*cm))
    story.append(sp())

    # Formula box
    formula_data = [[
        Paragraph("<b>I₁ / I₂  =  D₂² / D₁²</b>", 
                  ParagraphStyle("form", fontName="Helvetica-Bold", fontSize=13,
                                 textColor=NAVY, alignment=TA_CENTER)),
        Paragraph("Where:<br/>I = Intensity (radiation exposure rate)<br/>"
                  "D = Distance from source<br/>"
                  "Subscript 1 = original; 2 = new position",
                  ParagraphStyle("formn", fontName="Helvetica", fontSize=9,
                                 textColor=colors.black, leading=14)),
    ]]
    ft = Table(formula_data, colWidths=[CW*0.4, CW*0.6])
    ft.setStyle(TableStyle([
        ("BACKGROUND",    (0,0),(0,0), LIGHT_BG),
        ("BACKGROUND",    (1,0),(1,0), GREY_LT),
        ("GRID",          (0,0),(-1,-1), 1, TEAL),
        ("VALIGN",        (0,0),(-1,-1), "MIDDLE"),
        ("LEFTPADDING",   (0,0),(-1,-1), 10),
        ("TOPPADDING",    (0,0),(-1,-1), 10),
        ("BOTTOMPADDING", (0,0),(-1,-1), 10),
    ]))
    story.append(ft)
    story.append(sp())

    # Worked examples
    ex_data = [
        [Paragraph("Scenario", CELL_HDR), Paragraph("Given", CELL_HDR),
         Paragraph("Solution", CELL_HDR), Paragraph("Answer", CELL_HDR)],
        ["Distance doubled (d → 2d)", "I₁, D₁=d, D₂=2d",
         "I₂ = I₁ × (d/2d)² = I₁ × ¼", "Intensity = I/4  (75% reduction)"],
        ["Distance tripled (d → 3d)", "I₁, D₁=d, D₂=3d",
         "I₂ = I₁ × (d/3d)² = I₁ × 1/9", "Intensity = I/9  (89% reduction)"],
        ["Distance halved (d → d/2)", "I₁, D₁=d, D₂=d/2",
         "I₂ = I₁ × (d/0.5d)² = I₁ × 4", "Intensity = 4I  (quadrupled)"],
        ["Operator protection (6 ft rule)", "D=6 ft from source",
         "I₂ = I₁ × (1ft/6ft)² = I₁/36", "Intensity at 6 ft = 1/36th of source"],
        ["PID change: 8 in → 16 in", "mA, kVp constant; D doubles",
         "I₂ = I₁/4; compensate by 4× mAs", "Exposure time/mAs must be quadrupled"],
    ]
    story.append(box_table(ex_data, [3.5*cm, 3.0*cm, 5.0*cm, 3.3*cm], hdr_bg=TEAL))
    story.append(sp(1.5))

    # ═══════════════════════════════════════════════════════════════════════
    # SECTION 7 – EM SPECTRUM
    # ═══════════════════════════════════════════════════════════════════════
    story.append(hdr_para("  7.  ELECTROMAGNETIC SPECTRUM — X-RAY POSITION", bg=NAVY))
    story.append(sp())
    story.append(RadiationScaleDiagram(CW, 3.2*cm))
    story.append(sp())

    em_data = [
        [Paragraph("Property", CELL_HDR), Paragraph("X-Ray Values (Diagnostic)", CELL_HDR),
         Paragraph("Notes", CELL_HDR)],
        ["Wavelength", "0.01 – 0.5 nm  (10 – 500 pm)", "Shorter λ = harder beam = higher energy"],
        ["Frequency", "6×10¹⁷ – 3×10¹⁹ Hz", "f = c/λ"],
        ["Energy (keV)", "10 – 150 keV", "Dental: ~60-90 keV peak energy"],
        ["Speed", "3 × 10⁸ m/s (speed of light)", "Same for all EM radiation"],
        ["Nature", "Electromagnetic (transverse waves, photons)", "No mass, no charge"],
        ["Ionisation", "Yes — directly ionising", "Knocks orbital electrons → ion pairs"],
        ["Min. wavelength λmin", "λmin = 12.4 / kVp (in Angstroms)", "Duane-Hunt law; higher kVp → shorter λmin"],
    ]
    story.append(box_table(em_data, [3.5*cm, 5.5*cm, 5.8*cm], hdr_bg=TEAL))
    story.append(sp(1.5))

    # ═══════════════════════════════════════════════════════════════════════
    # SECTION 8 – BIOLOGICAL EFFECTS
    # ═══════════════════════════════════════════════════════════════════════
    story.append(hdr_para("  8.  BIOLOGICAL EFFECTS — CLASSIFICATION & THRESHOLDS", bg=NAVY))
    story.append(sp())

    bio_data = [
        [Paragraph("Feature", CELL_HDR), Paragraph("Stochastic Effects", CELL_HDR),
         Paragraph("Deterministic (Tissue) Effects", CELL_HDR)],
        ["Definition", "Probabilistic — chance of occurrence increases with dose",
         "Severity increases with dose once threshold exceeded"],
        ["Threshold dose", "NONE — any dose carries some risk",
         "YES — threshold must be exceeded"],
        ["Severity vs. dose", "Severity FIXED (does not change with dose)",
         "Severity INCREASES with dose above threshold"],
        ["Examples", "Cancer induction, genetic mutations, heritable effects",
         "Cataract (>0.5 Gy lens), epilation, skin erythema, radiation sickness, sterility"],
        ["Threshold doses", "None",
         "Temporary epilation: 3-5 Gy\nPermanent epilation: >7 Gy\nCataract: >0.5 Gy (ICRP 2011)\nAcute radiation sickness: >1 Gy whole body\nLD50/30: ~3-5 Gy whole body"],
        ["Protection principle", "ALARA — As Low As Reasonably Achievable",
         "Keep below threshold; dose limits set at fraction of threshold"],
        ["Relevant law", "Bergonie & Tribondeau law: more radiosensitive if rapidly dividing, undifferentiated, long mitotic future",
         "Same law applies; threshold-based protection is key"],
    ]
    story.append(box_table(bio_data, [3.0*cm, 5.5*cm, 6.3*cm], hdr_bg=RED_ACC))
    story.append(sp())

    # Cell sensitivity order
    story.append(hdr_para("  8a.  RADIOSENSITIVITY ORDER (HIGH → LOW)", bg=TEAL))
    story.append(sp())
    sens_data = [
        [Paragraph("Radiosensitivity", CELL_HDR), Paragraph("Cell / Tissue Types", CELL_HDR)],
        [Paragraph("HIGH", ParagraphStyle("hs", fontName="Helvetica-Bold", fontSize=8,
                   textColor=WHITE, alignment=TA_CENTER, leading=10)),
         "Lymphocytes, Bone marrow (stem cells), Gonads (spermatogonia, oocytes), "
         "Intestinal crypts, Lens epithelium"],
        [Paragraph("MODERATE", ParagraphStyle("ms", fontName="Helvetica-Bold", fontSize=8,
                   textColor=WHITE, alignment=TA_CENTER, leading=10)),
         "Salivary gland acinar cells (esp. serous/parotid), Endothelial cells, "
         "Fibroblasts, Oral mucosa, Skin basal cells"],
        [Paragraph("LOW", ParagraphStyle("ls", fontName="Helvetica-Bold", fontSize=8,
                   textColor=WHITE, alignment=TA_CENTER, leading=10)),
         "Muscle cells, Mature RBCs, Nerve cells (neurons), Cartilage, "
         "Mature bone (osteocytes)"],
    ]
    sens_t = Table(sens_data, colWidths=[2.5*cm, 12.3*cm])
    sens_t.setStyle(TableStyle([
        ("BACKGROUND",    (0,0), (-1,0), NAVY),
        ("TEXTCOLOR",     (0,0), (-1,0), WHITE),
        ("BACKGROUND",    (0,1), (0,1), RED_ACC),
        ("BACKGROUND",    (0,2), (0,2), ORANGE),
        ("BACKGROUND",    (0,3), (0,3), GREEN_ACC),
        ("TEXTCOLOR",     (0,1), (0,-1), WHITE),
        ("FONTNAME",      (1,1), (1,-1), "Helvetica"),
        ("FONTSIZE",      (0,0), (-1,-1), 8),
        ("GRID",          (0,0), (-1,-1), 0.5, GREY_MID),
        ("VALIGN",        (0,0), (-1,-1), "MIDDLE"),
        ("TOPPADDING",    (0,0), (-1,-1), 5),
        ("BOTTOMPADDING", (0,0), (-1,-1), 5),
        ("LEFTPADDING",   (0,0), (-1,-1), 6),
    ]))
    story.append(sens_t)
    story.append(sp(1.5))

    # ═══════════════════════════════════════════════════════════════════════
    # SECTION 9 – FACTORS CONTROLLING BEAM (SUMMARY)
    # ═══════════════════════════════════════════════════════════════════════
    story.append(PageBreak())
    story.append(hdr_para("  9.  FACTORS CONTROLLING X-RAY BEAM — QUALITY & QUANTITY", bg=NAVY))
    story.append(sp())

    ctrl_data = [
        [Paragraph("Factor", CELL_HDR), Paragraph("Affects Quality\n(Penetrating Power)", CELL_HDR),
         Paragraph("Affects Quantity\n(Number of Photons)", CELL_HDR),
         Paragraph("Effect on Patient Dose", CELL_HDR), Paragraph("Effect on Image", CELL_HDR)],
        [Paragraph("↑ kVp", CELL_BL), "YES — harder beam, shorter λ",
         "YES — also increases quantity", "↑ dose (but less skin dose if filter used)",
         "↓ contrast; ↑ grey shades"],
        [Paragraph("↑ mA", CELL_BL), "NO", "YES — more electrons → more X-rays",
         "↑ dose proportionally", "↑ density/darkness"],
        [Paragraph("↑ Time", CELL_BL), "NO", "YES — longer exposure = more X-rays",
         "↑ dose proportionally", "↑ density; ↑ motion blur risk"],
        [Paragraph("↑ mAs (mA×t)", CELL_BL), "NO", "YES", "↑ dose", "↑ density"],
        [Paragraph("↑ Filtration (Al)", CELL_BL), "YES — removes soft X-rays",
         "Slight ↓ (soft X-rays removed)", "↓ skin dose significantly",
         "Slight ↓ contrast; better quality"],
        [Paragraph("↑ Distance (SID)", CELL_BL), "NO", "YES — reduced by inverse sq. law",
         "↓ dose (1/d²)", "↓ magnification; ↑ sharpness"],
        [Paragraph("Rectangular collimation", CELL_BL), "NO", "NO",
         "↓ 60-70% vs round", "↓ scatter → ↑ contrast"],
        [Paragraph("↑ Target Z number", CELL_BL), "YES — more efficient X-ray production",
         "YES", "N/A (fixed by design)", "N/A"],
    ]
    story.append(box_table(ctrl_data, [2.5*cm, 2.8*cm, 2.8*cm, 2.8*cm, 3.9*cm], hdr_bg=TEAL))
    story.append(sp(1.5))

    # ═══════════════════════════════════════════════════════════════════════
    # SECTION 10 – RADIATION PROTECTION RULES
    # ═══════════════════════════════════════════════════════════════════════
    story.append(hdr_para("  10.  RADIATION PROTECTION — RULES, DISTANCES & GUIDELINES", bg=NAVY))
    story.append(sp())

    prot_data = [
        [Paragraph("Rule / Guideline", CELL_HDR), Paragraph("Requirement", CELL_HDR),
         Paragraph("Rationale", CELL_HDR)],
        ["Operator distance", "Minimum 6 feet (1.8 m) from X-ray tube",
         "Scatter radiation drops to negligible levels at 6 ft (inverse sq. law → 1/36 of source)"],
        ["Operator angle", "Stand at 90°–135° to primary beam",
         "Primary beam at 0°; scatter is least at 90°–135°; never stand in primary beam path"],
        ["Protective barriers", "2.5 mm Pb equivalent (primary barrier)\n1.5 mm Pb (secondary barrier)",
         "For walls/partitions between operator and X-ray source"],
        ["Lead apron for patient", "0.25 mm Pb equivalent (thyroid collar + apron)",
         "Protects thyroid (Wt=0.04) and bone marrow (Wt=0.12)"],
        ["Film holding devices", "ALWAYS use film holder / XCP / hemostat",
         "Never hold film with fingers in primary beam"],
        ["ALARA principle", "As Low As Reasonably Achievable",
         "Minimise dose with best technique, fastest film, digital sensors, rectangular collimation"],
        ["Pregnant patients", "Delay if possible; if essential — lead apron + thyroid collar mandatory",
         "Embryo/foetus highly radiosensitive (stochastic + deterministic risk)"],
        ["Film speed", "Use fastest available (F-speed / digital sensor)",
         "F-speed ~60% less dose than D-speed; digital 50-80% less than film"],
        ["CBCT restriction", "Only when conventional radiography inadequate",
         "CBCT doses 5–100x higher than OPG; justify each exposure (JUSTIFICATION principle)"],
    ]
    story.append(box_table(prot_data, [3.5*cm, 5.0*cm, 6.3*cm], hdr_bg=RED_ACC))
    story.append(sp(1.5))

    # ═══════════════════════════════════════════════════════════════════════
    # SECTION 11 – MNEMONICS & QUICK RECALL
    # ═══════════════════════════════════════════════════════════════════════
    story.append(hdr_para("  11.  MNEMONICS & EXAM QUICK-RECALL", bg=NAVY))
    story.append(sp())

    mnem_data = [
        [Paragraph("Topic", CELL_HDR), Paragraph("Mnemonic / Memory Aid", CELL_HDR),
         Paragraph("What it means", CELL_HDR)],
        ["X-ray tube components", "F-FAT (Filament, Focusing cup, Anode, Tube envelope)",
         "4 core components of the X-ray tube"],
        ["Clark's / SLOB rule", "SLOB = Same Lingual Opposite Buccal",
         "Object on LINGUAL side moves SAME direction as tube shift; BUCCAL moves OPPOSITE"],
        ["X-ray properties", "PIPE-SCIF\n(Penetrate, Ionise, Photographic, Excite-fluoresce,\nStraight lines, Cause bio damage, Invisible, Fast=c)",
         "All key properties of X-rays in one word"],
        ["Bergonie & Tribondeau", "MORE = More radiosensitive if:\nMitotic rate high, Oocyte-like (undifferentiated),\nReproduction likely (long mitotic future), Evolving rapidly",
         "Law of radiosensitivity"],
        ["Radiation units (old→SI)", "R → C/kg  |  rad → Gy  |  rem → Sv  |  Ci → Bq",
         "Divide rem/rad by 100 to get Sv/Gy; 1 Ci = 3.7×10¹⁰ Bq"],
        ["Filtration requirements", "<70 kVp = 1.5 mm Al  |  ≥70 kVp = 2.5 mm Al",
         "More kVp needs more filtration"],
        ["Stochastic vs deterministic", "S = no-threshold, Same severity; D = has threshold, Dose-dependent severity",
         "S for Stochastic = no Safe dose; D for Deterministic = Dose threshold"],
        ["Dose limit (worker)", "20 mSv/year average; max 50 mSv single year",
         "ICRP 2007 occupational limit"],
        ["Collimation dose saving", "Rectangular saves 60-70%; Round max 7 cm",
         "Always prefer rectangular for minimal dose"],
        ["Inverse square law quick calc", "2× distance → ¼ intensity; 3× → 1/9; ½ → 4×",
         "Square the ratio of distances (reciprocal)"],
    ]
    story.append(box_table(mnem_data, [3.2*cm, 5.5*cm, 6.1*cm], hdr_bg=PURPLE))
    story.append(sp(1.5))

    # ═══════════════════════════════════════════════════════════════════════
    # SECTION 12 – MASTER KEY-NUMBERS REFERENCE CARD
    # ═══════════════════════════════════════════════════════════════════════
    story.append(hdr_para("  12.  MASTER KEY-NUMBERS REFERENCE CARD", bg=TEAL))
    story.append(sp())

    kn_data = [
        [Paragraph("Parameter", CELL_HDR), Paragraph("Value", CELL_HDR),
         Paragraph("Parameter", CELL_HDR), Paragraph("Value", CELL_HDR)],
        ["Speed of X-rays", "3×10⁸ m/s",
         "Tungsten melting point", "3422°C"],
        ["Tungsten atomic number", "74",
         "Tungsten filament temp (thermionic emission)", "~2200°C"],
        ["Anode angle (dental)", "~20° (line focus principle)",
         "Dental kVp range", "60–90 kVp"],
        ["Total filtration <70 kVp", "≥1.5 mm Al equiv.",
         "Total filtration ≥70 kVp", "≥2.5 mm Al equiv."],
        ["Round collimator max dia.", "≤7 cm at skin",
         "Rect. collimation dose saving", "60–70%"],
        ["Operator distance", "≥6 feet (1.8 m)",
         "Operator angle to beam", "90–135°"],
        ["Occupational dose limit", "20 mSv/year (avg)",
         "Max single year (occupational)", "50 mSv"],
        ["Public dose limit", "1 mSv/year",
         "Pregnant worker abdominal limit", "1 mSv (remainder)"],
        ["Cataract threshold (ICRP 2011)", "0.5 Gy",
         "LD50/30 whole body", "~3–5 Gy"],
        ["1 Gy =", "100 rad",
         "1 Sv =", "100 rem"],
        ["1 Ci =", "3.7×10¹⁰ Bq",
         "1 R =", "2.58×10⁻⁴ C/kg"],
        ["Digital sensor dose saving vs F-film", "50–80% reduction",
         "F-speed vs D-speed saving", "~60% reduction"],
        ["Periapical dose (digital)", "~1–2 µSv",
         "OPG (panoramic) dose", "~14–24 µSv"],
        ["CBCT small FOV dose", "~40–100 µSv",
         "Annual background (India)", "~2400 µSv"],
        ["Duane-Hunt law", "λmin = 12.4 / kVp (Å)",
         "Wr for X-rays", "1"],
        ["Wt for thyroid", "0.04",
         "Wt for bone marrow", "0.12"],
    ]
    kn_t = Table(kn_data, colWidths=[CW*0.28, CW*0.22, CW*0.28, CW*0.22])
    kn_t.setStyle(TableStyle([
        ("BACKGROUND",    (0,0), (-1,0), NAVY),
        ("TEXTCOLOR",     (0,0), (-1,0), WHITE),
        ("FONTNAME",      (0,0), (-1,0), "Helvetica-Bold"),
        ("FONTSIZE",      (0,0), (-1,-1), 8),
        ("GRID",          (0,0), (-1,-1), 0.4, TEAL),
        ("ROWBACKGROUNDS",(0,1), (-1,-1), [WHITE, LIGHT_BG]),
        ("BACKGROUND",    (0,1), (0,-1), LIGHT_TL),
        ("BACKGROUND",    (2,1), (2,-1), LIGHT_TL),
        ("FONTNAME",      (0,1), (0,-1), "Helvetica-Bold"),
        ("FONTNAME",      (2,1), (2,-1), "Helvetica-Bold"),
        ("FONTSIZE",      (0,1), (0,-1), 7.5),
        ("FONTSIZE",      (2,1), (2,-1), 7.5),
        ("TOPPADDING",    (0,0), (-1,-1), 4),
        ("BOTTOMPADDING", (0,0), (-1,-1), 4),
        ("LEFTPADDING",   (0,0), (-1,-1), 5),
        ("VALIGN",        (0,0), (-1,-1), "MIDDLE"),
    ]))
    story.append(kn_t)
    story.append(sp(2))

    # Footer note
    story.append(HRFlowable(width=CW, thickness=1, color=TEAL))
    story.append(sp(0.5))
    story.append(Paragraph(
        "<font color='#c0392b'><b>Exam Tip:</b></font>  "
        "Always state BOTH old and SI units when asked. Mention ALARA, inverse square law, "
        "and rectangular collimation proactively in any radiation protection question. "
        "PDL space widening is the EARLIEST sign in jaw osteosarcoma. "
        "Rectangular collimation saves 60-70% dose compared to round.",
        NOTE_S))
    story.append(sp(0.3))
    story.append(Paragraph(
        "Prepared for BDS/MDS Oral Medicine & Oral Radiology Viva  |  Based on ICRP 2007 / NCRP / AERB / BDA Guidelines",
        FOOT_S))

    doc.build(story, onFirstPage=on_first_page, onLaterPages=on_page)
    print("PDF built successfully:", path)

build()
Running Command

cd /home/daytona/workspace/radiation-study && python3 build_pdf.py 2>&1

Running Command

python3 -c "from reportlab.pdfgen import canvas; c=canvas.Canvas('/tmp/t.pdf'); …

Editing File

~/radiation-study/build_pdf.py

Successfully replaced 1 instance(s) of the string in '/home/daytona/workspace/radiation-study/build_pdf.py'
Editing File

~/radiation-study/build_pdf.py

Successfully replaced 1 instance(s) of the string in '/home/daytona/workspace/radiation-study/build_pdf.py'
Running Command

cd /home/daytona/workspace/radiation-study && python3 build_pdf.py 2>&1

Running Command

ls -lh /home/daytona/workspace/radiation-study/Radiation_Study_Guide.pdf

Your study PDF is ready. Here's the download link:

Radiation Study Guide

PDF Document · PDF


What's inside the PDF (4 pages, colour-coded):
SectionContent
1. X-Ray Tube DiagramCustom labelled schematic (cathode, filament, focusing cup, anode, beam) + full components table with materials and functions
2. X-Ray ProductionBremsstrahlung vs Characteristic radiation comparison table
3. Radiation UnitsComplete old-unit → SI conversion table (R/C/kg, rad/Gy, rem/Sv, Ci/Bq) + Radiation Weighting Factors (Wr) + Tissue Weighting Factors (Wt)
4. Dose LimitsICRP 2007 occupational and public limits + typical dental radiograph doses (periapical, OPG, CBCT, etc.)
5. FiltrationFiltration types table + beam quality gradient bar + collimation types comparison
6. Inverse Square LawVisual diagram with dot-density representation + formula box + 5 worked examples
7. EM SpectrumColour-coded spectrum showing X-ray position + properties table with Duane-Hunt law
8. Biological EffectsStochastic vs deterministic table + radiosensitivity order (high/moderate/low)
9. Beam Control FactorsHow kVp, mA, time, filtration, distance each affect quality, quantity, dose, and image
10. Radiation ProtectionAll operator rules, distances, angles, barriers, ALARA
11. MnemonicsSLOB rule, PIPE-SCIF, Bergonie & Tribondeau memory aid, quick conversion tricks
12. Master Key-Numbers Card32 critical numbers in one reference table - the page to study the night before your viva
This is a shared conversation. Sign in to Orris to start your own chat.