Introduction to Achondroplasia Definition: Achondroplasia is the most common form of disproportionate dwarfism, particularly prevalent in India. Clinical Importance: Frequently featured in NEET PG examinations with at least one question every alternate year. Cause: Defect in endochondral ossification – the process responsible for lengthening bones, primarily affecting long bones, while intramembranous ossification (clavicle, skull, trunk) remains normal. Genetic Basis and Inheritance Inheritance Pattern: Autosomal Dominant inheritance. However, >80% of cases result from new mutations (sporadic). Gene Involved: Mutation in the FGFR3 gene (Fibroblast Growth Factor Receptor 3). Important differential: FGFR2 gene mutation causes Apert syndrome, not achondroplasia. Gene Condition Associated Notes FGFR3 Achondroplasia Most common cause FGFR2 Apert syndrome Different skeletal disorder Fibrillin-1 Marfan syndrome Not related to achondroplasia COL5A1 (Type 1 Collagen) Osteogenesis Imperfecta Not related NOTCH1 Some leukemias No orthopedic connection Risk Factors Paternal Age: Risk increases if the father is older than 36 years. Maternal Age: High maternal age is associated with other conditions like Down syndrome, not achondroplasia. Pathophysiology and Bone Ossification Endochondral Ossification: Defective in achondroplasia, affecting lengthening of long bones (arms and legs). Intramembranous Ossification: Normal; explains why the skull, clavicle, and trunk appear normal or relatively larger. Clinical Features and Morphology Body Proportions and Growth Patterns Dwarfism Type: Rhizomelic dwarfism – shortening predominantly of the proximal limbs (upper arms and thighs). Limb Proportions: Shortened upper limbs more than forearms. Normal trunk length due to normal intramembranous ossification. Head and Face: Macrocephaly (large head size). Frontal bossing: prominent forehead. Peculiar nose shape due to normal nasal bone. Skeletal Characteristics Feature Description Skull Large (macrocephaly), frontal bossing Limbs Short, stocky tubular bones Trunk Normal size, maintains normal length Hands Trident hand with middle and ring finger divergence, starfish appearance due to uniform finger length Pelvis Champagne glass appearance: pelvis is wider than long, complicates vaginal delivery Vertebrae Bullet-shaped (beaked) vertebrae Hand Appearance: Trident/Starfish Sign Middle finger is shortened. The thumb does not appear much shorter, which causes the fingers to look of similar length — this mimics the look of a trident or starfish. The ring and middle fingers diverge, creating a trident-like hand. Radiological Features of Achondroplasia Radiological Feature Description and Clinical Relevance Bullet-shaped vertebrae Vertebrae appear beaked on lateral X-rays Trident hand Diverged fingers, especially middle & ring fingers Short, broad tubular bones Tibia, femur, humerus appear short and thick Champagne glass pelvis Broad iliac wings, narrow pelvic inlet, impacts delivery Clinical Complications Reproductive challenges: Difficulty in vaginal delivery due to pelvic abnormalities. Cesarean section commonly required for childbirth. Summary of Key Distinguishing Clinical and Genetic Features Aspect Achondroplasia Inheritance Autosomal dominant (majority new mutations) Gene FGFR3 Ossification defect Endochondral (affects long bones) Limb proportion Rhizomelic shortening (upper limbs more affected) Skull/Trunk Normal size (intramembranous ossification unaffected) Radiology Bullet vertebrae, trident hand, champagne pelvis Cognitive impact No mental retardation Common NEET PG Exam Questions & Answers Question Correct Answer / Explanation Mutation responsible for achondroplasia FGFR3 gene Gene causing Marfan syndrome Fibrillin-1 gene Mode of inheritance Autosomal dominant Presence of mental retardation Absent in achondroplasia Characteristic hand appearance Trident/starfish hand Pelvic shape Champagne glass pelvis, complicates vaginal delivery me notes of these both resources combineds, dont give exam highyield points. Convert the following text into highly structured, visually engaging study notes while strictly preserving the original content. Core Content Rules (Mandatory) Do NOT delete any concept, sentence, value, example, or fact. Do NOT add new information, explanations, or examples. Do NOT merge separate ideas into one. Maintain the same logical order as the original text. Every concept from the original text must appear in the final notes. Formatting Rules Headings Structure — Organize content using Main Headings → Subheadings → Nested bullet hierarchy. Convert long paragraphs into clear structured sections. Sentence Conversion — Convert long sentences → bullet points. Convert redundant sentences → separate short bullets. Do not merge or remove them. Visual Style — Use relevant emojis sparingly. Clean academic medical note style. Do NOT use decorative separators or horizontal lines after every topic. Highlighting Rules: Bold → very important concepts or keywords Italic → supporting key terms Bold + Italic → exam-important facts Tables — If text contains comparisons, classifications, drug lists, diagnostic criteria, scores, or mechanisms → convert into fully developed tables with horizontal and vertical borders that remain intact in MS Word/Google Docs. No simplified or collapsed tables. Difference tables use format: Feature | Item 1 | Item 2 | Item 3 Flowcharts — If text describes steps, processes, pathways, or sequences → convert into arrow format: Step 1 → Step 2 → Step 3 → Final outcome Page Density (IMPORTANT): Use compact font sizes (body text 9pt, bullets 9pt, table cells 8.5pt, headings 13pt max) Use tight spacing between sections (minimal gaps) Use narrow margins (1.4 cm left/right, 1.5 cm top/bottom) Goal: fit maximum content per page while keeping it readable Visual Layout Restrictions — Do NOT: add decorative separators, summarize content, rewrite meaning, paraphrase core statements. Final Output: High-quality medical revision notes Visually structured, easy for exam revision Professional PDF format Footer on every page: Made By Sohaib Khan SMC 5 and minimize the size of pic according to page size so that it take less space and less number of pages give me pdf and also add the pics which i mentioned with respective sections, dont give me FMGE , INICET, NEETPG ,AIIMS etc label
mkdir -p /tmp/workspace/achondroplasia && cd /tmp/workspace/achondroplasia && curl -s -o img1_lecture.jpg "https://d361swluvexzqs.cloudfront.net/chats-attachments/Screenshot-2026-07-18-225154-1784397893889-kxcnqo.jpg" && curl -s -o img2_trident_hand.jpg "https://d361swluvexzqs.cloudfront.net/chats-attachments/images-17-1784397911748-jtm7fn.jpeg" && curl -s -o img3_champagne_pelvis.gif "https://d361swluvexzqs.cloudfront.net/chats-attachments/980343-1784397916033-3cd5k9.gif" && curl -s -o img4_bullet_vertebrae.jpg "https://d361swluvexzqs.cloudfront.net/chats-attachments/images-18-1784397919736-mqvxxe.jpeg" && echo "Done" && ls -lh
cd /tmp/workspace/achondroplasia && python3 -c " from PIL import Image # Convert GIF to PNG for use in PDF img = Image.open('img3_champagne_pelvis.gif') img = img.convert('RGB') img.save('img3_champagne_pelvis.png') print('GIF converted to PNG') # Check all image sizes for fn in ['img1_lecture.jpg','img2_trident_hand.jpg','img3_champagne_pelvis.png','img4_bullet_vertebrae.jpg']: im = Image.open(fn) print(f'{fn}: {im.size}') "
~/achondroplasia/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.lib.enums import TA_LEFT, TA_CENTER, TA_RIGHT
from reportlab.platypus import (
SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
KeepTogether, Image as RLImage, HRFlowable
)
from reportlab.platypus.flowables import Flowable
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
from reportlab.graphics.shapes import Drawing, Rect, String, Line, Arrow
from reportlab.graphics import renderPDF
import os
# ── page setup ──────────────────────────────────────────────────────────────
PAGE_W, PAGE_H = A4 # 595.27 x 841.89 pt
LM = RM = 1.4 * cm
TM = BM = 1.5 * cm
CONTENT_W = PAGE_W - LM - RM
# ── colours ─────────────────────────────────────────────────────────────────
C_DARK_BLUE = colors.HexColor("#0D2B5E")
C_MID_BLUE = colors.HexColor("#1A4A8A")
C_LIGHT_BLUE = colors.HexColor("#D6E4F7")
C_ACCENT = colors.HexColor("#E8501A")
C_HEADER_BG = colors.HexColor("#1A4A8A")
C_ROW_ALT = colors.HexColor("#EEF4FB")
C_ROW_HEAD = colors.HexColor("#C4D9F0")
C_WHITE = colors.white
C_BLACK = colors.black
C_GREY = colors.HexColor("#555555")
C_LIGHT_GREY = colors.HexColor("#F5F5F5")
C_ARROW = colors.HexColor("#1A4A8A")
C_BOX_BG = colors.HexColor("#EEF4FB")
# ── styles ───────────────────────────────────────────────────────────────────
styles = getSampleStyleSheet()
def S(name, **kw):
return ParagraphStyle(name, **kw)
TITLE = S("TITLE",
fontSize=15, leading=18, textColor=C_WHITE,
fontName="Helvetica-Bold", alignment=TA_CENTER,
spaceAfter=2)
H1 = S("H1",
fontSize=12, leading=14, textColor=C_WHITE,
fontName="Helvetica-Bold", alignment=TA_LEFT,
spaceBefore=4, spaceAfter=2)
H2 = S("H2",
fontSize=10, leading=12.5, textColor=C_MID_BLUE,
fontName="Helvetica-Bold", alignment=TA_LEFT,
spaceBefore=5, spaceAfter=1)
H3 = S("H3",
fontSize=9, leading=11, textColor=C_DARK_BLUE,
fontName="Helvetica-Bold", alignment=TA_LEFT,
spaceBefore=3, spaceAfter=1)
BODY = S("BODY",
fontSize=9, leading=11.5, textColor=C_BLACK,
fontName="Helvetica", alignment=TA_LEFT,
spaceAfter=1)
BODY_BOLD = S("BODY_BOLD",
fontSize=9, leading=11.5, textColor=C_BLACK,
fontName="Helvetica-Bold", alignment=TA_LEFT,
spaceAfter=1)
BULLET = S("BULLET",
fontSize=9, leading=11.5, textColor=C_BLACK,
fontName="Helvetica", alignment=TA_LEFT,
leftIndent=10, bulletIndent=2,
spaceAfter=1)
BULLET2 = S("BULLET2",
fontSize=9, leading=11.5, textColor=C_BLACK,
fontName="Helvetica", alignment=TA_LEFT,
leftIndent=22, bulletIndent=14,
spaceAfter=1)
CAPTION = S("CAPTION",
fontSize=8, leading=10, textColor=C_GREY,
fontName="Helvetica-Oblique", alignment=TA_CENTER,
spaceAfter=2)
FOOTER_STYLE = S("FOOTER",
fontSize=7.5, leading=9, textColor=C_GREY,
fontName="Helvetica", alignment=TA_CENTER)
CELL = S("CELL",
fontSize=8.5, leading=10.5, textColor=C_BLACK,
fontName="Helvetica", alignment=TA_LEFT)
CELL_BOLD = S("CELL_BOLD",
fontSize=8.5, leading=10.5, textColor=C_DARK_BLUE,
fontName="Helvetica-Bold", alignment=TA_LEFT)
CELL_HEAD = S("CELL_HEAD",
fontSize=8.5, leading=10.5, textColor=C_WHITE,
fontName="Helvetica-Bold", alignment=TA_CENTER)
# ── helpers ──────────────────────────────────────────────────────────────────
def b(text): return f"<b>{text}</b>"
def i(text): return f"<i>{text}</i>"
def bi(text): return f"<b><i>{text}</i></b>"
def section_header(title, emoji=""):
"""Blue bar section header as a Table row."""
label = f"{emoji} {title}" if emoji else title
data = [[Paragraph(label, H1)]]
t = Table(data, colWidths=[CONTENT_W])
t.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,-1), C_HEADER_BG),
('LEFTPADDING', (0,0), (-1,-1), 6),
('RIGHTPADDING', (0,0), (-1,-1), 6),
('TOPPADDING', (0,0), (-1,-1), 4),
('BOTTOMPADDING',(0,0), (-1,-1), 4),
('ROUNDEDCORNERS', [4]),
]))
return t
def sub_header(title):
return Paragraph(title, H2)
def sub3(title):
return Paragraph(title, H3)
def body(text):
return Paragraph(text, BODY)
def bullet(text, level=1):
style = BULLET if level == 1 else BULLET2
bullet_char = "•" if level == 1 else "–"
return Paragraph(f"{bullet_char} {text}", style)
def sp(h=4):
return Spacer(1, h)
def make_table(headers, rows, col_widths=None, zebra=True):
"""Create a bordered table with header row."""
if col_widths is None:
n = len(headers)
col_widths = [CONTENT_W / n] * n
data = [[Paragraph(h, CELL_HEAD) for h in headers]]
for i, row in enumerate(rows):
data.append([Paragraph(str(c), CELL) for c in row])
t = Table(data, colWidths=col_widths)
style = [
# header
('BACKGROUND', (0,0), (-1,0), C_HEADER_BG),
('TEXTCOLOR', (0,0), (-1,0), C_WHITE),
('FONTNAME', (0,0), (-1,0), 'Helvetica-Bold'),
('FONTSIZE', (0,0), (-1,0), 8.5),
('ALIGN', (0,0), (-1,0), 'CENTER'),
# body
('FONTNAME', (0,1), (-1,-1), 'Helvetica'),
('FONTSIZE', (0,1), (-1,-1), 8.5),
('ALIGN', (0,1), (-1,-1), 'LEFT'),
# grid
('GRID', (0,0), (-1,-1), 0.5, colors.HexColor("#B0C8E8")),
('BOX', (0,0), (-1,-1), 1, C_MID_BLUE),
# padding
('LEFTPADDING', (0,0), (-1,-1), 5),
('RIGHTPADDING', (0,0), (-1,-1), 5),
('TOPPADDING', (0,0), (-1,-1), 3),
('BOTTOMPADDING', (0,0), (-1,-1), 3),
('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
]
if zebra:
for r in range(1, len(data)):
if r % 2 == 0:
style.append(('BACKGROUND', (0,r), (-1,r), C_ROW_ALT))
t.setStyle(TableStyle(style))
return t
def flow_chart(steps, arrow="→"):
"""Render a horizontal flowchart as a table of steps connected by arrows."""
# Build single row: [step, arrow, step, arrow, ...]
items = []
for idx, step in enumerate(steps):
items.append(step)
if idx < len(steps) - 1:
items.append(arrow)
# Decide col widths
n_steps = len(steps)
n_arrows = n_steps - 1
step_w = (CONTENT_W - n_arrows * 1.0*cm) / n_steps
col_widths = []
for idx in range(len(items)):
col_widths.append(step_w if idx % 2 == 0 else 1.0*cm)
data_row = []
for idx, item in enumerate(items):
if idx % 2 == 0:
data_row.append(Paragraph(item, S(f"FC{idx}", fontSize=8.5,
leading=10.5, fontName="Helvetica", alignment=TA_CENTER,
textColor=C_DARK_BLUE)))
else:
data_row.append(Paragraph(item, S(f"AR{idx}", fontSize=11,
leading=12, fontName="Helvetica-Bold",
alignment=TA_CENTER, textColor=C_ACCENT)))
t = Table([data_row], colWidths=col_widths)
style_cmds = [
('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
('ALIGN', (0,0), (-1,-1), 'CENTER'),
('LEFTPADDING', (0,0), (-1,-1), 4),
('RIGHTPADDING', (0,0), (-1,-1), 4),
('TOPPADDING', (0,0), (-1,-1), 5),
('BOTTOMPADDING', (0,0), (-1,-1), 5),
]
for idx in range(0, len(items), 2):
col = idx
style_cmds.append(('BACKGROUND', (col,0), (col,0), C_BOX_BG))
style_cmds.append(('BOX', (col,0), (col,0), 0.6, C_MID_BLUE))
style_cmds.append(('ROUNDEDCORNERS', [3]))
t.setStyle(TableStyle(style_cmds))
return t
def note_box(text):
"""Highlighted note box."""
data = [[Paragraph(f"📌 {text}", S("NB",
fontSize=8.5, leading=10.5,
fontName="Helvetica-Bold",
textColor=C_DARK_BLUE))]]
t = Table(data, colWidths=[CONTENT_W])
t.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,-1), colors.HexColor("#FFF3CD")),
('BOX', (0,0), (-1,-1), 0.8, colors.HexColor("#E8A000")),
('LEFTPADDING', (0,0), (-1,-1), 6),
('RIGHTPADDING', (0,0), (-1,-1), 6),
('TOPPADDING', (0,0), (-1,-1), 4),
('BOTTOMPADDING', (0,0), (-1,-1), 4),
]))
return t
def fit_image(path, max_w, max_h):
"""Return a ReportLab Image scaled to fit within max_w x max_h."""
from PIL import Image as PILImage
pil = PILImage.open(path)
w, h = pil.size
ratio = min(max_w / w, max_h / h)
return RLImage(path, width=w*ratio, height=h*ratio)
# ── footer ────────────────────────────────────────────────────────────────────
def footer_canvas(canvas, doc):
canvas.saveState()
canvas.setFont("Helvetica", 7.5)
canvas.setFillColor(C_GREY)
footer_text = "Made By Sohaib Khan | SMC 5"
canvas.drawCentredString(PAGE_W / 2, BM * 0.55, footer_text)
# thin line above footer
canvas.setStrokeColor(colors.HexColor("#CCCCCC"))
canvas.setLineWidth(0.5)
canvas.line(LM, BM * 0.8, PAGE_W - RM, BM * 0.8)
# page number right
canvas.drawRightString(PAGE_W - RM, BM * 0.55, f"Page {doc.page}")
canvas.restoreState()
# ── document build ─────────────────────────────────────────────────────────
IMG_DIR = "/tmp/workspace/achondroplasia"
OUT_PATH = "/tmp/workspace/achondroplasia/Achondroplasia_Notes.pdf"
doc = SimpleDocTemplate(
OUT_PATH,
pagesize=A4,
leftMargin=LM, rightMargin=RM,
topMargin=TM, bottomMargin=BM + 0.8*cm,
title="Achondroplasia – Structured Study Notes",
author="Sohaib Khan SMC 5",
)
story = []
# ══════════════════════════════════════════════════════════════════════════════
# TITLE BLOCK
# ══════════════════════════════════════════════════════════════════════════════
title_data = [[Paragraph("🦴 ACHONDROPLASIA", TITLE)],
[Paragraph("Structured Study Notes | Skeletal Dysplasia", S("SUB",
fontSize=9, leading=11, textColor=colors.HexColor("#A8C8F0"),
fontName="Helvetica-Oblique", alignment=TA_CENTER))]]
title_table = Table(title_data, colWidths=[CONTENT_W])
title_table.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,-1), C_DARK_BLUE),
('LEFTPADDING', (0,0), (-1,-1), 8),
('RIGHTPADDING', (0,0), (-1,-1), 8),
('TOPPADDING', (0,0), (-1,-1), 8),
('BOTTOMPADDING', (0,0), (-1,-1), 8),
]))
story.append(title_table)
story.append(sp(6))
# ══════════════════════════════════════════════════════════════════════════════
# SECTION 1 INTRODUCTION & DEFINITION
# ══════════════════════════════════════════════════════════════════════════════
story.append(section_header("1. INTRODUCTION & DEFINITION", "📖"))
story.append(sp(3))
# lecture image alongside intro text
img1 = fit_image(os.path.join(IMG_DIR, "img1_lecture.jpg"), max_w=7.5*cm, max_h=5*cm)
intro_text = [
[Paragraph(
"<b>Definition:</b> Achondroplasia is the <b><i>most common form of disproportionate dwarfism</i></b>, "
"particularly prevalent in India.", BODY),
sp(2),
Paragraph("<b>Type:</b> Skeletal dysplasia (failure of endochondral ossification).", BODY),
sp(2),
Paragraph(
"<b>Clinical Importance:</b> Frequently featured in <b>NEET PG examinations</b> – at least "
"one question every alternate year.", BODY),
]
]
# side-by-side: text left, image right
col_text = CONTENT_W - 8*cm
row_data = [[
[
Paragraph("<b>Definition:</b> Achondroplasia is the "
"<b><i>most common form of disproportionate dwarfism</i></b>, "
"particularly prevalent in India.", BODY),
sp(3),
Paragraph("<b>Type:</b> Skeletal dysplasia.", BODY),
sp(3),
Paragraph("<b>Clinical Importance:</b> Frequently featured in <b>NEET PG examinations</b> – "
"at least one question every alternate year.", BODY),
sp(3),
Paragraph(
"<b>Cause:</b> Defect in <b><i>endochondral ossification</i></b> – the process responsible "
"for lengthening bones, primarily affecting <b>long bones</b>.", BODY),
sp(3),
Paragraph(
"<b>Intramembranous ossification</b> (clavicle, skull, trunk) remains <b>normal</b>.", BODY),
],
img1,
]]
layout = Table(row_data, colWidths=[col_text, 7.8*cm])
layout.setStyle(TableStyle([
('VALIGN', (0,0), (-1,-1), 'TOP'),
('LEFTPADDING', (0,0), (-1,-1), 0),
('RIGHTPADDING', (0,0), (-1,-1), 0),
('TOPPADDING', (0,0), (-1,-1), 0),
('BOTTOMPADDING', (0,0), (-1,-1), 0),
]))
story.append(layout)
story.append(Paragraph("<i>Fig 1: Achondroplasia – Skeletal Dysplasia lecture overview</i>", CAPTION))
story.append(sp(6))
# ══════════════════════════════════════════════════════════════════════════════
# SECTION 2 GENETIC BASIS & INHERITANCE
# ══════════════════════════════════════════════════════════════════════════════
story.append(section_header("2. GENETIC BASIS & INHERITANCE", "🧬"))
story.append(sp(4))
story.append(sub_header("2a. Inheritance Pattern"))
story.append(bullet(b("Autosomal Dominant") + " inheritance."))
story.append(bullet(bi(">80% of cases") + " result from " + b("new mutations") + " (sporadic) – not inherited."))
story.append(sp(3))
story.append(sub_header("2b. Gene Involved"))
story.append(bullet("Mutation in the " + bi("FGFR3 gene") + " (Fibroblast Growth Factor Receptor 3)."))
story.append(bullet(b("Important differential:") + " " + b("FGFR2") + " gene mutation causes " + bi("Apert syndrome") + ", NOT achondroplasia."))
story.append(sp(4))
story.append(sub_header("2c. Gene–Condition Comparison Table"))
story.append(sp(2))
gene_headers = ["Gene", "Condition Associated", "Notes"]
gene_rows = [
[b("FGFR3"), bi("Achondroplasia"), "Most common cause"],
["FGFR2", "Apert syndrome", "Different skeletal disorder"],
["Fibrillin-1", "Marfan syndrome", "Not related to achondroplasia"],
["COL5A1 (Type 1 Collagen)", "Osteogenesis Imperfecta", "Not related"],
["NOTCH1", "Some leukemias", "No orthopedic connection"],
]
story.append(make_table(gene_headers, gene_rows,
col_widths=[5.5*cm, 6.5*cm, CONTENT_W - 12*cm]))
story.append(sp(6))
# ══════════════════════════════════════════════════════════════════════════════
# SECTION 3 RISK FACTORS
# ══════════════════════════════════════════════════════════════════════════════
story.append(section_header("3. RISK FACTORS", "⚠️"))
story.append(sp(4))
rf_headers = ["Risk Factor", "Association", "Note"]
rf_rows = [
[bi("Paternal Age > 36 years"), b("Achondroplasia") + " (risk increases)", "New FGFR3 mutations more common with older fathers"],
["High Maternal Age", "Down syndrome", b("NOT") + " associated with achondroplasia"],
]
story.append(make_table(rf_headers, rf_rows,
col_widths=[5.5*cm, 5.5*cm, CONTENT_W - 11*cm]))
story.append(sp(6))
# ══════════════════════════════════════════════════════════════════════════════
# SECTION 4 PATHOPHYSIOLOGY & BONE OSSIFICATION
# ══════════════════════════════════════════════════════════════════════════════
story.append(section_header("4. PATHOPHYSIOLOGY & BONE OSSIFICATION", "🔬"))
story.append(sp(4))
story.append(sub_header("4a. Ossification Types"))
oss_headers = ["Ossification Type", "Status in Achondroplasia", "Structures Affected"]
oss_rows = [
[bi("Endochondral Ossification"), b("DEFECTIVE"), "Long bones – arms, legs (lengthening affected)"],
[bi("Intramembranous Ossification"), b("NORMAL"), "Skull, clavicle, trunk (appear normal/relatively larger)"],
]
story.append(make_table(oss_headers, oss_rows,
col_widths=[5.5*cm, 4.5*cm, CONTENT_W - 10*cm]))
story.append(sp(4))
story.append(sub_header("4b. Pathophysiology Flowchart"))
story.append(sp(3))
story.append(flow_chart([
"FGFR3 Mutation",
"Defective\nEndochondral\nOssification",
"Impaired\nLong Bone\nLengthening",
"Proximal\nLimb\nShortening",
"Rhizomelic\nDwarfism",
]))
story.append(sp(6))
# ══════════════════════════════════════════════════════════════════════════════
# SECTION 5 CLINICAL FEATURES & MORPHOLOGY
# ══════════════════════════════════════════════════════════════════════════════
story.append(section_header("5. CLINICAL FEATURES & MORPHOLOGY", "🩺"))
story.append(sp(4))
story.append(sub_header("5a. Body Proportions & Growth Patterns"))
story.append(bullet(b("Dwarfism Type:") + " " + bi("Rhizomelic dwarfism") + " – shortening predominantly of the " + b("proximal limbs") + " (upper arms and thighs)."))
story.append(bullet(b("Limb Proportions:")))
story.append(bullet("Shortened " + b("upper limbs") + " more than forearms.", level=2))
story.append(bullet(b("Normal trunk length") + " due to normal intramembranous ossification.", level=2))
story.append(bullet(b("Arm span is decreased."), level=2))
story.append(bullet(b("Head and Face:")))
story.append(bullet(bi("Macrocephaly") + " (large head size).", level=2))
story.append(bullet(bi("Frontal bossing:") + " prominent forehead.", level=2))
story.append(bullet("Peculiar nose shape due to normal nasal bone.", level=2))
story.append(bullet(b("No mental retardation") + " – cognitive function is normal."))
story.append(sp(4))
story.append(sub_header("5b. Skeletal Characteristics – Summary Table"))
story.append(sp(2))
skel_headers = ["Structure / Feature", "Description"]
skel_rows = [
[b("Skull"), "Large (" + bi("macrocephaly") + "), frontal bossing; " + bi("Foramen magnum is narrow")] ,
[b("Limbs"), "Short, stocky tubular bones"],
[b("Trunk"), "Normal size, maintains normal length"],
[b("Hands"), bi("Trident hand") + " – middle & ring finger divergence; " + bi("starfish appearance") + " due to uniform finger length"],
[b("Pelvis"), bi("Champagne glass appearance") + ": pelvis wider than long; complicates vaginal delivery"],
[b("Vertebrae"), bi("Bullet-shaped (beaked) vertebrae") + "; scalloping of vertebrae"],
[b("Spine"), "Foramen magnum is narrow; scalloping of vertebrae"],
]
story.append(make_table(skel_headers, skel_rows,
col_widths=[4.5*cm, CONTENT_W - 4.5*cm]))
story.append(sp(4))
# ── 5c Trident / Starfish Hand ───────────────────────────────────────────────
story.append(sub_header("5c. Hand Appearance – Trident / Starfish Sign 🤚"))
story.append(sp(3))
img2 = fit_image(os.path.join(IMG_DIR, "img2_trident_hand.jpg"), max_w=5*cm, max_h=6*cm)
hand_text = [
Paragraph(bullet("Middle finger is shortened.", level=1).text if False else
"• Middle finger is shortened.", BULLET),
Paragraph("• The thumb does not appear much shorter → fingers appear of " + b("similar length") + ".", BULLET),
Paragraph("• This mimics the look of a " + bi("trident") + " or " + bi("starfish") + ".", BULLET),
Paragraph("• The " + b("ring and middle fingers diverge") + ", creating a trident-like hand.", BULLET),
]
hand_layout = Table(
[[hand_text, img2]],
colWidths=[CONTENT_W - 5.5*cm, 5.5*cm]
)
hand_layout.setStyle(TableStyle([
('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
('LEFTPADDING', (0,0), (-1,-1), 0),
('RIGHTPADDING', (0,0), (-1,-1), 0),
('TOPPADDING', (0,0), (-1,-1), 0),
('BOTTOMPADDING', (0,0), (-1,-1), 0),
]))
story.append(hand_layout)
story.append(Paragraph("<i>Fig 2: Trident / Starfish hand in Achondroplasia</i>", CAPTION))
story.append(sp(6))
# ══════════════════════════════════════════════════════════════════════════════
# SECTION 6 RADIOLOGICAL FEATURES
# ══════════════════════════════════════════════════════════════════════════════
story.append(section_header("6. RADIOLOGICAL FEATURES", "🩻"))
story.append(sp(4))
story.append(sub_header("6a. Radiological Features Table"))
story.append(sp(2))
rad_headers = ["Radiological Feature", "Description & Clinical Relevance"]
rad_rows = [
[bi("Bullet-shaped (beaked) vertebrae"), "Vertebrae appear beaked on lateral X-rays; also described as scalloping"],
[bi("Trident hand"), "Diverged fingers, especially middle & ring fingers"],
[bi("Short, broad tubular bones"), "Tibia, femur, humerus appear short and thick"],
[bi("Champagne glass pelvis"), "Broad iliac wings, narrow pelvic inlet – impacts vaginal delivery"],
]
story.append(make_table(rad_headers, rad_rows,
col_widths=[5.5*cm, CONTENT_W - 5.5*cm]))
story.append(sp(5))
story.append(sub_header("6b. Champagne Glass Pelvis"))
story.append(sp(3))
img3 = fit_image(os.path.join(IMG_DIR, "img3_champagne_pelvis.png"), max_w=CONTENT_W, max_h=6.5*cm)
story.append(img3)
story.append(Paragraph("<i>Fig 3: Champagne Glass Pelvis – X-ray showing broad iliac wings with narrow pelvic inlet (arrows), compared to champagne glass shape</i>", CAPTION))
story.append(sp(5))
story.append(sub_header("6c. Bullet-Shaped (Beaked) Vertebrae"))
story.append(sp(3))
img4 = fit_image(os.path.join(IMG_DIR, "img4_bullet_vertebrae.jpg"), max_w=6*cm, max_h=8*cm)
vert_text = [
Paragraph("• Vertebrae appear " + bi("beaked / bullet-shaped") + " on lateral X-ray.", BULLET),
sp(3),
Paragraph("• This is a " + b("hallmark radiological sign") + " of achondroplasia.", BULLET),
sp(3),
Paragraph("• Also described as " + bi("scalloping of vertebrae") + ".", BULLET),
sp(3),
Paragraph("• White arrows (solid) point to beaked vertebrae;", BULLET),
Paragraph(" outlined arrows point to adjacent affected segments.", BULLET2),
]
vert_layout = Table([[vert_text, img4]],
colWidths=[CONTENT_W - 6.5*cm, 6.5*cm])
vert_layout.setStyle(TableStyle([
('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
('LEFTPADDING', (0,0), (-1,-1), 0),
('RIGHTPADDING', (0,0), (-1,-1), 0),
('TOPPADDING', (0,0), (-1,-1), 0),
('BOTTOMPADDING', (0,0), (-1,-1), 0),
]))
story.append(vert_layout)
story.append(Paragraph("<i>Fig 4: Lateral spine X-ray showing bullet/beaked vertebrae in Achondroplasia</i>", CAPTION))
story.append(sp(6))
# ══════════════════════════════════════════════════════════════════════════════
# SECTION 7 CLINICAL COMPLICATIONS
# ══════════════════════════════════════════════════════════════════════════════
story.append(section_header("7. CLINICAL COMPLICATIONS", "⚕️"))
story.append(sp(4))
story.append(sub_header("7a. Reproductive Challenges"))
story.append(bullet("Difficulty in " + b("vaginal delivery") + " due to pelvic abnormalities (champagne glass pelvis)."))
story.append(bullet(bi("Cesarean section") + " commonly required for childbirth."))
story.append(sp(6))
# ══════════════════════════════════════════════════════════════════════════════
# SECTION 8 SUMMARY TABLE
# ══════════════════════════════════════════════════════════════════════════════
story.append(section_header("8. KEY SUMMARY – DISTINGUISHING FEATURES", "📋"))
story.append(sp(4))
sum_headers = ["Aspect", "Achondroplasia"]
sum_rows = [
[b("Inheritance"), "Autosomal dominant (majority " + bi("new mutations") + ")"],
[b("Gene"), bi("FGFR3")],
[b("Ossification Defect"), bi("Endochondral") + " (affects long bones)"],
[b("Limb Proportion"), bi("Rhizomelic shortening") + " (upper limbs more affected)"],
[b("Skull / Trunk"), "Normal size (intramembranous ossification unaffected); " + b("foramen magnum narrow")],
[b("Radiology"), "Bullet vertebrae, trident hand, champagne pelvis"],
[b("Cognitive Impact"), b("No mental retardation")],
[b("Delivery"), b("Cesarean section") + " often required"],
[b("Paternal Age Risk"), "Father > " + bi("36 years") + " → increased risk of new FGFR3 mutation"],
]
story.append(make_table(sum_headers, sum_rows,
col_widths=[5*cm, CONTENT_W - 5*cm]))
story.append(sp(6))
# ══════════════════════════════════════════════════════════════════════════════
# SECTION 9 COMMON Q&A (without exam labels)
# ══════════════════════════════════════════════════════════════════════════════
story.append(section_header("9. COMMON QUESTIONS & ANSWERS", "❓"))
story.append(sp(4))
qa_headers = ["Question", "Correct Answer / Explanation"]
qa_rows = [
["Mutation responsible for achondroplasia", bi("FGFR3 gene")],
["Gene causing Marfan syndrome", bi("Fibrillin-1 gene")],
["Mode of inheritance", b("Autosomal dominant")],
["Presence of mental retardation", b("Absent") + " in achondroplasia"],
["Characteristic hand appearance", bi("Trident / Starfish hand")],
["Pelvic shape", bi("Champagne glass pelvis") + " – complicates vaginal delivery"],
]
story.append(make_table(qa_headers, qa_rows,
col_widths=[8*cm, CONTENT_W - 8*cm]))
story.append(sp(6))
# ══════════════════════════════════════════════════════════════════════════════
# NOTE BOX
# ══════════════════════════════════════════════════════════════════════════════
story.append(note_box(
"Key differentials: FGFR3 → Achondroplasia | FGFR2 → Apert Syndrome | "
"Fibrillin-1 → Marfan | COL5A1 → Osteogenesis Imperfecta | "
"High MATERNAL age → Down Syndrome (NOT Achondroplasia)"
))
# ── build ─────────────────────────────────────────────────────────────────────
doc.build(story, onFirstPage=footer_canvas, onLaterPages=footer_canvas)
print(f"PDF written: {OUT_PATH}")
cd /tmp/workspace/achondroplasia && python3 build_pdf.py 2>&1
~/achondroplasia/build_pdf.py
Successfully replaced 1 instance(s) of the string in '/tmp/workspace/achondroplasia/build_pdf.py'
cd /tmp/workspace/achondroplasia && python3 build_pdf.py 2>&1
python3 -c " from pypdf import PdfReader r = PdfReader('/tmp/workspace/achondroplasia/Achondroplasia_Notes.pdf') print(f'Pages: {len(r.pages)}') import os print(f'Size: {os.path.getsize(\"/tmp/workspace/achondroplasia/Achondroplasia_Notes.pdf\")/1024:.1f} KB') "
Achondroplasia Notes
PDF Document · PDF
Introduction to Osteogenesis Imperfecta Also known as: Brittle Bone Disease Lobstein’s Disease (less commonly used) Frequently tested in NEET PG exams—typically 1-2 questions every cycle. Crucial to differentiate from other bone disorders, especially Marble Bone Disease. Differentiation from Marble Bone Disease Aspect Osteogenesis Imperfecta Marble Bone Disease Bone Quality Qualitative defect in Type 1 collagen Excessive bone formation Bone Density Normal quantity but defective integrity Increased, dense bone Pathophysiology Mutation in Type 1 collagen gene (COL1A1/A2) Failure of osteoclastic activity Clinical Presentation Multiple fractures, ligament laxity Bone sclerosis, brittle but dense bone Genetic and Molecular Basis Type 1 collagen mutation is central. Mutation occurs in COL1A1 or COL1A2 genes, encoding collagen type 1 alpha chains. Collagen type 1 is critical for structural integrity in: Bone Skin Ligaments Other connective tissues Inheritance pattern: Mostly autosomal dominant (most common for exam focus). Rarely autosomal recessive or non-Mendelian forms exist. Pathophysiology Flowchart Copy COL1A1/COL1A2 gene mutation ↓ Defective Type 1 collagen synthesis ↓ Weak structural integrity of bone & connective tissues ↓ Increased bone fragility and clinical manifestations Clinical Features Orthopedic Manifestations Multiple fractures following minor trauma, often in neonates, children, and adolescents. Fracture frequency decreases after adolescence but rises post-menopause. Most commonly affected bones: Lower limbs > Upper limbs Femur > Tibia > Other bones (like humerus, forearm, skull) Ligamentous laxity leads to repeated dislocations: Hip dislocation Patellar dislocation Radial head dislocation Wormian bones: Multiple bone fragments within cranial sutures (especially lambdoid sutures). Non-Orthopedic Manifestations System Manifestation Explanation Eyes Blue sclera Thin, translucent sclera reveals underlying uveal pigmentation Saturn’s ring (white ring around cornea) Deposits seen in the corneal periphery Hyperopia Refractive error Retinal detachments Due to weakened connective tissue structure Ears Hearing loss (conductive & sensorineural) Conductive: otosclerosis of middle ear bones; Sensorineural: 8th nerve compression Teeth Dentogenesis imperfecta Hypoplastic, fragile teeth, especially lower incisors; enamel usually normal Skin & Muscle Fragile skin, easy bruising Poor collagen weakens skin integrity Muscle hypertonia Muscle-bone imbalance contributes to fractures Other Malignant hyperthermia, heat intolerance Increased sweating and susceptibility to heat strokes Diagnostic Approach Genetic Diagnosis Gold standard: Genetic analysis of skin fibroblasts. Techniques: PCR amplification Gel electrophoresis to detect COL1A1/COL1A2 mutations Radiological Diagnosis Presence of fractures in multiple bones at various stages of healing. Differential diagnosis includes child abuse, but genetic and clinical context distinguishes OI. Biochemical Markers Marker Observation in OI Notes Alkaline phosphatase Mild elevation due to active bone repair Significantly higher in osteosarcoma Acid phosphatase No relation Not useful Treatment Modalities Treatment Mechanism/Indication Notes Gene therapy Attempts to replace/repair defective COL1A gene Experimental, emerging therapy Bisphosphonates Inhibit osteoclasts, increase bone density Pamidronate is the drug of choice, reduces fracture rate Surgical management Internal fixation with rods, plates to prevent fractures Supportive and symptomatic Supportive care Pain management, physiotherapy Improves quality of life Summary Table: Osteogenesis Imperfecta Highlights Aspect Details Synonyms Brittle Bone Disease, Lobstein’s disease Etiology Mutation in COL1A1/COL1A2 gene (Type 1 collagen) Inheritance Primarily autosomal dominant Key Clinical Features Multiple fractures, blue sclera, ligament laxity, dentinogenesis imperfecta, hearing loss Diagnosis Genetic testing, radiographs showing fractures at different healing stages, mild alkaline phosphatase elevation Treatment Bisphosphonates (Pamidronate), gene therapy (experimental), surgical fixation dont give exam highyield points. Convert the following text into highly structured, visually engaging study notes while strictly preserving the original content. Core Content Rules (Mandatory) Do NOT delete any concept, sentence, value, example, or fact. Do NOT add new information, explanations, or examples. Do NOT merge separate ideas into one. Maintain the same logical order as the original text. Every concept from the original text must appear in the final notes. Formatting Rules Headings Structure — Organize content using Main Headings → Subheadings → Nested bullet hierarchy. Convert long paragraphs into clear structured sections. Sentence Conversion — Convert long sentences → bullet points. Convert redundant sentences → separate short bullets. Do not merge or remove them. Visual Style — Use relevant emojis sparingly. Clean academic medical note style. Do NOT use decorative separators or horizontal lines after every topic. Highlighting Rules: Bold → very important concepts or keywords Italic → supporting key terms Bold + Italic → exam-important facts Tables — If text contains comparisons, classifications, drug lists, diagnostic criteria, scores, or mechanisms → convert into fully developed tables with horizontal and vertical borders that remain intact in MS Word/Google Docs. No simplified or collapsed tables. Difference tables use format: Feature | Item 1 | Item 2 | Item 3 Flowcharts — If text describes steps, processes, pathways, or sequences → convert into arrow format: Step 1 → Step 2 → Step 3 → Final outcome Page Density (IMPORTANT): Use compact font sizes (body text 9pt, bullets 9pt, table cells 8.5pt, headings 13pt max) Use tight spacing between sections (minimal gaps) Use narrow margins (1.4 cm left/right, 1.5 cm top/bottom) Goal: fit maximum content per page while keeping it readable Visual Layout Restrictions — Do NOT: add decorative separators, summarize content, rewrite meaning, paraphrase core statements. Final Output: High-quality medical revision notes Visually structured, easy for exam revision Professional PDF format Footer on every page: Made By Sohaib Khan SMC 5 and minimize the size of pic according to page size so that it take less space and less number of pages give me pdf and also add the pics which i mentioned with respective sections, dont give me FMGE , INICET, NEETPG ,AIIMS etc label from everyhting
osteogenesis imperfecta blue sclera clinical photo
osteogenesis imperfecta wormian bones X-ray skull
osteogenesis imperfecta dentinogenesis imperfecta teeth photo
mkdir -p /tmp/workspace/oi && cd /tmp/workspace/oi # Blue sclera curl -s -o img1_blue_sclera.jpg "https://upload.medbullets.com/topic/112036/images/characteristically_blue_sclerae_of_patient_with_osteogenesis_imperfecta.jpg" & # Wormian bones X-ray curl -s -o img2_wormian_bones.jpg "https://www.share4rare.org/sites/default/files/inline-images/Figure%205.jpg" & # Dentinogenesis imperfecta curl -s -o img3_dentinogenesis.jpg "https://www.share4rare.org/sites/default/files/inline-images/dentinogenesis%20imperfecta.jpg" & # Another wormian bones X-ray (backup) curl -s -o img4_wormian2.png "https://www.researchgate.net/profile/Derya-Erdogan/publication/260431062/figure/fig5/AS:601655590547499@1520457279695/Arrows-demonstrate-the-Wormian-bones-on-skull-X-ray.png" & wait ls -lh
cd /tmp/workspace/oi && python3 -c " from PIL import Image import os for fn in ['img1_blue_sclera.jpg','img2_wormian_bones.jpg','img3_dentinogenesis.jpg']: try: im = Image.open(fn) print(f'{fn}: {im.size} mode={im.mode}') except Exception as e: print(f'{fn}: ERROR {e}') "
~/oi/build_oi_pdf.py
from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.units import cm
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.enums import TA_LEFT, TA_CENTER
from reportlab.platypus import (
SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
KeepTogether, Image as RLImage,
)
import os
PAGE_W, PAGE_H = A4
LM = RM = 1.4 * cm
TM = BM = 1.5 * cm
CONTENT_W = PAGE_W - LM - RM
# ── colours ──────────────────────────────────────────────────────────────────
C_DARK = colors.HexColor("#1A3A5C")
C_MID = colors.HexColor("#1F5FA6")
C_LIGHT = colors.HexColor("#D6E8FA")
C_ACCENT = colors.HexColor("#C0392B")
C_ROW_ALT = colors.HexColor("#EEF4FB")
C_NOTEBG = colors.HexColor("#FFF3CD")
C_NOTEBDR = colors.HexColor("#E8A000")
C_GREY = colors.HexColor("#555555")
C_WHITE = colors.white
C_BLACK = colors.black
C_FLOWBG = colors.HexColor("#EBF4FF")
# ── paragraph styles ──────────────────────────────────────────────────────────
def S(name, **kw):
return ParagraphStyle(name, **kw)
TITLE = S("TITLE", fontSize=15, leading=18, textColor=C_WHITE,
fontName="Helvetica-Bold", alignment=TA_CENTER, spaceAfter=2)
SUBTITLE = S("SUBTITLE", fontSize=8.5, leading=10, textColor=colors.HexColor("#A8C8F0"),
fontName="Helvetica-Oblique", alignment=TA_CENTER)
H1 = S("H1", fontSize=11, leading=13, textColor=C_WHITE,
fontName="Helvetica-Bold", alignment=TA_LEFT, spaceBefore=3, spaceAfter=2)
H2 = S("H2", fontSize=10, leading=12, textColor=C_MID,
fontName="Helvetica-Bold", alignment=TA_LEFT, spaceBefore=4, spaceAfter=1)
H3 = S("H3", fontSize=9, leading=11, textColor=C_DARK,
fontName="Helvetica-Bold", alignment=TA_LEFT, spaceBefore=2, spaceAfter=1)
BODY = S("BODY", fontSize=9, leading=11.5, textColor=C_BLACK,
fontName="Helvetica", alignment=TA_LEFT, spaceAfter=1)
BULLET = S("BULLET", fontSize=9, leading=11.5, textColor=C_BLACK,
fontName="Helvetica", alignment=TA_LEFT, leftIndent=10, bulletIndent=2, spaceAfter=1)
BULLET2= S("BULLET2",fontSize=9, leading=11.5, textColor=C_BLACK,
fontName="Helvetica", alignment=TA_LEFT, leftIndent=22, bulletIndent=14, spaceAfter=1)
CAPTION= S("CAPTION",fontSize=8, leading=10, textColor=C_GREY,
fontName="Helvetica-Oblique", alignment=TA_CENTER, spaceAfter=2)
CELL = S("CELL", fontSize=8.5,leading=10.5, textColor=C_BLACK,
fontName="Helvetica", alignment=TA_LEFT)
CELL_H = S("CELL_H", fontSize=8.5,leading=10.5, textColor=C_WHITE,
fontName="Helvetica-Bold", alignment=TA_CENTER)
FLOW_S = S("FLOW_S", fontSize=8.5,leading=10.5, textColor=C_DARK,
fontName="Helvetica", alignment=TA_CENTER)
ARROW_S= S("ARROW_S",fontSize=12, leading=13, textColor=C_ACCENT,
fontName="Helvetica-Bold", alignment=TA_CENTER)
def b(t): return f"<b>{t}</b>"
def i(t): return f"<i>{t}</i>"
def bi(t): return f"<b><i>{t}</i></b>"
# ── builders ──────────────────────────────────────────────────────────────────
def sec(title, emoji=""):
label = f"{emoji} {title}" if emoji else title
t = Table([[Paragraph(label, H1)]], colWidths=[CONTENT_W])
t.setStyle(TableStyle([
('BACKGROUND', (0,0),(-1,-1), C_MID),
('LEFTPADDING',(0,0),(-1,-1), 6),
('RIGHTPADDING',(0,0),(-1,-1), 6),
('TOPPADDING',(0,0),(-1,-1), 4),
('BOTTOMPADDING',(0,0),(-1,-1), 4),
]))
return t
def sub(t): return Paragraph(t, H2)
def sub3(t): return Paragraph(t, H3)
def body(t): return Paragraph(t, BODY)
def bul(text, level=1):
st = BULLET if level == 1 else BULLET2
ch = "•" if level == 1 else "–"
return Paragraph(f"{ch} {text}", st)
def sp(h=4): return Spacer(1, h)
def tbl(headers, rows, widths=None, zebra=True):
if widths is None:
n = len(headers)
widths = [CONTENT_W / n] * n
data = [[Paragraph(h, CELL_H) for h in headers]]
for row in rows:
data.append([Paragraph(str(c), CELL) for c in row])
ts = [
('BACKGROUND',(0,0),(-1,0), C_MID),
('TEXTCOLOR',(0,0),(-1,0), C_WHITE),
('FONTNAME',(0,0),(-1,0),'Helvetica-Bold'),
('FONTSIZE',(0,0),(-1,0), 8.5),
('ALIGN',(0,0),(-1,0),'CENTER'),
('FONTNAME',(0,1),(-1,-1),'Helvetica'),
('FONTSIZE',(0,1),(-1,-1), 8.5),
('ALIGN',(0,1),(-1,-1),'LEFT'),
('GRID',(0,0),(-1,-1), 0.5, colors.HexColor("#B0C8E8")),
('BOX',(0,0),(-1,-1), 1, C_MID),
('LEFTPADDING',(0,0),(-1,-1), 5),
('RIGHTPADDING',(0,0),(-1,-1), 5),
('TOPPADDING',(0,0),(-1,-1), 3),
('BOTTOMPADDING',(0,0),(-1,-1), 3),
('VALIGN',(0,0),(-1,-1),'MIDDLE'),
]
if zebra:
for r in range(1, len(data)):
if r % 2 == 0:
ts.append(('BACKGROUND',(0,r),(-1,r), C_ROW_ALT))
t = Table(data, colWidths=widths)
t.setStyle(TableStyle(ts))
return t
def flowchart(steps):
items = []
for idx, s in enumerate(steps):
items.append(s)
if idx < len(steps) - 1:
items.append("→")
n_s = len(steps)
n_a = n_s - 1
sw = (CONTENT_W - n_a * 1.0*cm) / n_s
cw = []
for idx in range(len(items)):
cw.append(sw if idx % 2 == 0 else 1.0*cm)
row = []
for idx, item in enumerate(items):
if idx % 2 == 0:
row.append(Paragraph(item, FLOW_S))
else:
row.append(Paragraph(item, ARROW_S))
t = Table([row], colWidths=cw)
ts = [
('VALIGN',(0,0),(-1,-1),'MIDDLE'),
('ALIGN',(0,0),(-1,-1),'CENTER'),
('LEFTPADDING',(0,0),(-1,-1), 4),
('RIGHTPADDING',(0,0),(-1,-1), 4),
('TOPPADDING',(0,0),(-1,-1), 5),
('BOTTOMPADDING',(0,0),(-1,-1), 5),
]
for idx in range(0, len(items), 2):
ts.append(('BACKGROUND',(idx,0),(idx,0), C_FLOWBG))
ts.append(('BOX',(idx,0),(idx,0), 0.6, C_MID))
t.setStyle(TableStyle(ts))
return t
def note(text):
t = Table([[Paragraph(f"📌 {text}", S("NB", fontSize=8.5, leading=10.5,
fontName="Helvetica-Bold", textColor=C_DARK))]], colWidths=[CONTENT_W])
t.setStyle(TableStyle([
('BACKGROUND',(0,0),(-1,-1), C_NOTEBG),
('BOX',(0,0),(-1,-1), 0.8, C_NOTEBDR),
('LEFTPADDING',(0,0),(-1,-1), 6),
('RIGHTPADDING',(0,0),(-1,-1), 6),
('TOPPADDING',(0,0),(-1,-1), 4),
('BOTTOMPADDING',(0,0),(-1,-1), 4),
]))
return t
def fit_img(path, max_w, max_h):
from PIL import Image as PILImg
pil = PILImg.open(path)
w, h = pil.size
r = min(max_w / w, max_h / h)
return RLImage(path, width=w*r, height=h*r)
def footer(canvas, doc):
canvas.saveState()
canvas.setFont("Helvetica", 7.5)
canvas.setFillColor(C_GREY)
canvas.drawCentredString(PAGE_W/2, BM*0.55, "Made By Sohaib Khan | SMC 5")
canvas.setStrokeColor(colors.HexColor("#CCCCCC"))
canvas.setLineWidth(0.5)
canvas.line(LM, BM*0.8, PAGE_W-RM, BM*0.8)
canvas.drawRightString(PAGE_W-RM, BM*0.55, f"Page {doc.page}")
canvas.restoreState()
# ── document ──────────────────────────────────────────────────────────────────
IMG = "/tmp/workspace/oi"
OUT = "/tmp/workspace/oi/OsteogenesisImperfecta_Notes.pdf"
doc = SimpleDocTemplate(OUT, pagesize=A4,
leftMargin=LM, rightMargin=RM,
topMargin=TM, bottomMargin=BM+0.8*cm,
title="Osteogenesis Imperfecta – Structured Study Notes",
author="Sohaib Khan SMC 5")
story = []
# ── TITLE BLOCK ───────────────────────────────────────────────────────────────
title_tbl = Table([
[Paragraph("🦴 OSTEOGENESIS IMPERFECTA", TITLE)],
[Paragraph("Brittle Bone Disease | Structured Study Notes", SUBTITLE)],
], colWidths=[CONTENT_W])
title_tbl.setStyle(TableStyle([
('BACKGROUND',(0,0),(-1,-1), C_DARK),
('LEFTPADDING',(0,0),(-1,-1), 8),
('RIGHTPADDING',(0,0),(-1,-1), 8),
('TOPPADDING',(0,0),(-1,-1), 8),
('BOTTOMPADDING',(0,0),(-1,-1), 8),
]))
story.append(title_tbl)
story.append(sp(6))
# ══════════════════════════════════════════════════════════════════════════════
# SECTION 1 INTRODUCTION
# ══════════════════════════════════════════════════════════════════════════════
story.append(sec("1. INTRODUCTION & OVERVIEW", "📖"))
story.append(sp(3))
story.append(sub("1a. Also Known As"))
story.append(bul(bi("Brittle Bone Disease")))
story.append(bul(b("Lobstein's Disease") + " (less commonly used)"))
story.append(sp(3))
story.append(sub("1b. Clinical Importance"))
story.append(bul("Frequently tested in " + b("NEET PG exams") + " – typically " + bi("1–2 questions every cycle.")))
story.append(bul(b("Crucial to differentiate") + " from other bone disorders, especially " + bi("Marble Bone Disease.")))
story.append(sp(6))
# ══════════════════════════════════════════════════════════════════════════════
# SECTION 2 DIFFERENTIATION TABLE
# ══════════════════════════════════════════════════════════════════════════════
story.append(sec("2. DIFFERENTIATION: OI vs MARBLE BONE DISEASE", "🔍"))
story.append(sp(4))
diff_h = ["Feature", bi("Osteogenesis Imperfecta"), bi("Marble Bone Disease")]
diff_r = [
[b("Bone Quality"), "Qualitative defect in Type 1 collagen", "Excessive bone formation"],
[b("Bone Density"), "Normal quantity but " + b("defective integrity"), b("Increased") + ", dense bone"],
[b("Pathophysiology"), "Mutation in Type 1 collagen gene (COL1A1/A2)", "Failure of osteoclastic activity"],
[b("Clinical Presentation"), "Multiple fractures, ligament laxity", "Bone sclerosis, brittle but dense bone"],
]
story.append(tbl(diff_h, diff_r, widths=[4*cm, 7.5*cm, CONTENT_W-11.5*cm]))
story.append(sp(6))
# ══════════════════════════════════════════════════════════════════════════════
# SECTION 3 GENETIC & MOLECULAR BASIS
# ══════════════════════════════════════════════════════════════════════════════
story.append(sec("3. GENETIC & MOLECULAR BASIS", "🧬"))
story.append(sp(3))
story.append(bul(bi("Type 1 collagen mutation") + " is central."))
story.append(bul("Mutation occurs in " + bi("COL1A1") + " or " + bi("COL1A2") + " genes, encoding collagen type 1 alpha chains."))
story.append(bul(b("Collagen type 1") + " is critical for structural integrity in:"))
story.append(bul("Bone", level=2))
story.append(bul("Skin", level=2))
story.append(bul("Ligaments", level=2))
story.append(bul("Other connective tissues", level=2))
story.append(sp(3))
story.append(sub("3a. Inheritance Pattern"))
story.append(bul(b("Mostly " + bi("Autosomal Dominant")) + " (most common for exam focus)."))
story.append(bul("Rarely " + i("autosomal recessive") + " or " + i("non-Mendelian") + " forms exist."))
story.append(sp(6))
# ══════════════════════════════════════════════════════════════════════════════
# SECTION 4 PATHOPHYSIOLOGY FLOWCHART
# ══════════════════════════════════════════════════════════════════════════════
story.append(sec("4. PATHOPHYSIOLOGY FLOWCHART", "🔬"))
story.append(sp(4))
story.append(flowchart([
"COL1A1 / COL1A2\nGene Mutation",
"Defective Type 1\nCollagen Synthesis",
"Weak Structural\nIntegrity of Bone\n& Connective Tissue",
"Increased Bone\nFragility",
"Clinical\nManifestations",
]))
story.append(sp(6))
# ══════════════════════════════════════════════════════════════════════════════
# SECTION 5 CLINICAL FEATURES
# ══════════════════════════════════════════════════════════════════════════════
story.append(sec("5. CLINICAL FEATURES", "🩺"))
story.append(sp(4))
story.append(sub("5a. Orthopaedic Manifestations"))
story.append(bul(b("Multiple fractures") + " following minor trauma – often in " + i("neonates, children, and adolescents.")))
story.append(bul("Fracture frequency " + b("decreases after adolescence") + " but " + b("rises post-menopause.")))
story.append(bul(b("Most commonly affected bones:")))
story.append(bul(bi("Lower limbs > Upper limbs"), level=2))
story.append(bul(bi("Femur > Tibia") + " > Other bones (humerus, forearm, skull)", level=2))
story.append(bul(b("Ligamentous laxity") + " leads to repeated dislocations:"))
story.append(bul(i("Hip dislocation"), level=2))
story.append(bul(i("Patellar dislocation"), level=2))
story.append(bul(i("Radial head dislocation"), level=2))
story.append(bul(bi("Wormian bones:") + " Multiple bone fragments within cranial sutures (especially " + b("lambdoid sutures.)")))
story.append(sp(4))
# Wormian bones image
img2 = fit_img(os.path.join(IMG, "img2_wormian_bones.jpg"), max_w=CONTENT_W, max_h=5.5*cm)
story.append(img2)
story.append(Paragraph("<i>Fig 1: Wormian bones in OI – multiple small accessory bones within cranial sutures on skull X-ray (arrows)</i>", CAPTION))
story.append(sp(5))
story.append(sub("5b. Non-Orthopaedic Manifestations"))
story.append(sp(2))
nonortho_h = ["System", b("Manifestation"), "Explanation"]
nonortho_r = [
[b("Eyes"),
bi("Blue sclera"),
"Thin, translucent sclera reveals underlying " + i("uveal pigmentation")],
["",
bi("Saturn's ring") + " (white ring around cornea)",
"Deposits seen in corneal periphery"],
["",
i("Hyperopia"),
"Refractive error"],
["",
i("Retinal detachments"),
"Due to weakened connective tissue structure"],
[b("Ears"),
bi("Hearing loss") + " (conductive & sensorineural)",
b("Conductive:") + " otosclerosis of middle ear bones; " + b("Sensorineural:") + " 8th nerve compression"],
[b("Teeth"),
bi("Dentogenesis imperfecta"),
"Hypoplastic, fragile teeth, especially " + b("lower incisors") + "; enamel usually normal"],
[b("Skin & Muscle"),
i("Fragile skin, easy bruising"),
"Poor collagen weakens skin integrity"],
["",
i("Muscle hypertonia"),
"Muscle–bone imbalance contributes to fractures"],
[b("Other"),
bi("Malignant hyperthermia") + ", heat intolerance",
"Increased sweating and susceptibility to heat strokes"],
]
story.append(tbl(nonortho_h, nonortho_r,
widths=[2.5*cm, 5.5*cm, CONTENT_W-8*cm]))
story.append(sp(4))
# Blue sclera image + Dentinogenesis image side by side
img1 = fit_img(os.path.join(IMG, "img1_blue_sclera.jpg"), max_w=8.5*cm, max_h=3.5*cm)
img3 = fit_img(os.path.join(IMG, "img3_dentinogenesis.jpg"), max_w=8.5*cm, max_h=3.5*cm)
img_row = Table(
[[img1, img3]],
colWidths=[CONTENT_W/2, CONTENT_W/2]
)
img_row.setStyle(TableStyle([
('VALIGN',(0,0),(-1,-1),'MIDDLE'),
('ALIGN',(0,0),(-1,-1),'CENTER'),
('LEFTPADDING',(0,0),(-1,-1), 2),
('RIGHTPADDING',(0,0),(-1,-1), 2),
('TOPPADDING',(0,0),(-1,-1), 0),
('BOTTOMPADDING',(0,0),(-1,-1), 0),
]))
story.append(img_row)
cap_row = Table(
[[Paragraph("<i>Fig 2: Blue sclera – thin translucent sclera revealing uveal pigment</i>", CAPTION),
Paragraph("<i>Fig 3: Dentinogenesis imperfecta – hypoplastic, fragile, discoloured teeth</i>", CAPTION)]],
colWidths=[CONTENT_W/2, CONTENT_W/2]
)
cap_row.setStyle(TableStyle([
('LEFTPADDING',(0,0),(-1,-1), 2),
('RIGHTPADDING',(0,0),(-1,-1), 2),
('TOPPADDING',(0,0),(-1,-1), 1),
('BOTTOMPADDING',(0,0),(-1,-1), 0),
]))
story.append(cap_row)
story.append(sp(6))
# ══════════════════════════════════════════════════════════════════════════════
# SECTION 6 DIAGNOSTIC APPROACH
# ══════════════════════════════════════════════════════════════════════════════
story.append(sec("6. DIAGNOSTIC APPROACH", "🔎"))
story.append(sp(4))
story.append(sub("6a. Genetic Diagnosis"))
story.append(bul(bi("Gold standard:") + " Genetic analysis of " + b("skin fibroblasts.")))
story.append(bul(b("Techniques:")))
story.append(bul(i("PCR amplification"), level=2))
story.append(bul(i("Gel electrophoresis") + " to detect " + b("COL1A1 / COL1A2") + " mutations", level=2))
story.append(sp(3))
story.append(sub("6b. Radiological Diagnosis"))
story.append(bul("Presence of " + b("fractures in multiple bones") + " at " + bi("various stages of healing.")))
story.append(bul("Differential diagnosis includes " + i("child abuse") + " – genetic and clinical context distinguishes OI."))
story.append(sp(3))
story.append(sub("6c. Biochemical Markers"))
story.append(sp(2))
bio_h = ["Marker", "Observation in OI", "Notes"]
bio_r = [
[bi("Alkaline phosphatase"), b("Mild elevation") + " due to active bone repair", "Significantly higher in osteosarcoma"],
["Acid phosphatase", "No relation", b("Not useful")],
]
story.append(tbl(bio_h, bio_r, widths=[4.5*cm, 6*cm, CONTENT_W-10.5*cm]))
story.append(sp(6))
# ══════════════════════════════════════════════════════════════════════════════
# SECTION 7 TREATMENT
# ══════════════════════════════════════════════════════════════════════════════
story.append(sec("7. TREATMENT MODALITIES", "💊"))
story.append(sp(4))
tx_h = ["Treatment", "Mechanism / Indication", "Notes"]
tx_r = [
[b("Gene therapy"),
"Attempts to replace/repair defective COL1A gene",
"Experimental, emerging therapy"],
[bi("Bisphosphonates"),
"Inhibit osteoclasts, increase bone density",
bi("Pamidronate") + " is the " + b("drug of choice") + " – reduces fracture rate"],
[b("Surgical management"),
"Internal fixation with rods, plates to prevent fractures",
"Supportive and symptomatic"],
[b("Supportive care"),
"Pain management, physiotherapy",
"Improves quality of life"],
]
story.append(tbl(tx_h, tx_r, widths=[4.5*cm, 7*cm, CONTENT_W-11.5*cm]))
story.append(sp(6))
# ══════════════════════════════════════════════════════════════════════════════
# SECTION 8 SUMMARY TABLE
# ══════════════════════════════════════════════════════════════════════════════
story.append(sec("8. SUMMARY – KEY HIGHLIGHTS", "📋"))
story.append(sp(4))
sum_h = ["Aspect", "Details"]
sum_r = [
[b("Synonyms"), bi("Brittle Bone Disease") + ", Lobstein's disease"],
[b("Etiology"), "Mutation in " + bi("COL1A1 / COL1A2") + " gene (Type 1 collagen)"],
[b("Inheritance"), "Primarily " + bi("Autosomal Dominant")],
[b("Key Clinical Features"), "Multiple fractures, " + bi("blue sclera") + ", ligament laxity, "
+ bi("dentinogenesis imperfecta") + ", hearing loss"],
[b("Diagnosis"), "Genetic testing, radiographs showing fractures at different healing stages, "
"mild " + bi("alkaline phosphatase elevation")],
[b("Treatment"), bi("Bisphosphonates (Pamidronate)") + ", gene therapy (experimental), surgical fixation"],
]
story.append(tbl(sum_h, sum_r, widths=[4.5*cm, CONTENT_W-4.5*cm]))
story.append(sp(5))
story.append(note(
"OI: COL1A1/COL1A2 mutation → qualitative collagen defect → fragile bones | "
"Drug of choice: Pamidronate (bisphosphonate) | "
"Gold standard diagnosis: Skin fibroblast genetic analysis | "
"Key sign: Blue sclera + Wormian bones + Dentinogenesis imperfecta"
))
doc.build(story, onFirstPage=footer, onLaterPages=footer)
print(f"PDF written: {OUT}")
cd /tmp/workspace/oi && python3 build_oi_pdf.py 2>&1
python3 -c " from pypdf import PdfReader import os r = PdfReader('/tmp/workspace/oi/OsteogenesisImperfecta_Notes.pdf') print(f'Pages: {len(r.pages)}') print(f'Size: {os.path.getsize(\"/tmp/workspace/oi/OsteogenesisImperfecta_Notes.pdf\")/1024:.1f} KB') "
OsteogenesisImperfecta Notes
PDF Document · PDF
give me paget disease in orthopedic in same format notes as osteogenesis imperfecta in pdf
Paget disease bone orthopedic clinical features pathophysiology treatment NEET PG
Paget disease bone X-ray cotton wool skull picture of skull sign
Paget disease bone bowing tibia photo clinical X-ray flame sign
mkdir -p /tmp/workspace/paget && cd /tmp/workspace/paget # Cotton wool skull X-ray curl -sL -o img1_cotton_wool.jpg "https://prod-images-static.radiopaedia.org/images/14071529/02d6d26c8b765d4b24b0093485fd21ee56c762dca3088a6de0ca81531a7ba56d_big_gallery.jpeg" & # Bowing tibia X-ray curl -sL -o img2_tibia_xray.jpg "https://upload.orthobullets.com/topic/8040/images/paget%27s%20disease%20tibia.jpg" & # Flame/blade of grass sign curl -sL -o img3_flame_sign.jpg "https://prod-images-static.radiopaedia.org/images/7865863/cd84a8620e903f19b8adc87c7589e8_big_gallery.jpeg" & # Pelvis / general Paget histology curl -sL -o img4_mosaic.jpg "https://cdn.ncbi.nlm.nih.gov/pmc/blobs/dfd4/6850043/f12451d30656/JBMR-34-579-g002.jpg" & wait ls -lh
python3 -c " from PIL import Image import os for fn in ['img1_cotton_wool.jpg','img2_tibia_xray.jpg','img3_flame_sign.jpg','img4_mosaic.jpg']: try: im = Image.open(f'/tmp/workspace/paget/{fn}') print(f'{fn}: {im.size} mode={im.mode}') except Exception as e: print(f'{fn}: ERROR {e}') "
~/paget/build_paget_pdf.py
from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.units import cm
from reportlab.lib.styles import ParagraphStyle
from reportlab.lib.enums import TA_LEFT, TA_CENTER
from reportlab.platypus import (
SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle, Image as RLImage,
)
import os
PAGE_W, PAGE_H = A4
LM = RM = 1.4 * cm
TM = BM = 1.5 * cm
CONTENT_W = PAGE_W - LM - RM
C_DARK = colors.HexColor("#1A3A5C")
C_MID = colors.HexColor("#1F5FA6")
C_ACCENT = colors.HexColor("#C0392B")
C_ROW = colors.HexColor("#EEF4FB")
C_NOTEBG = colors.HexColor("#FFF3CD")
C_NOTEBDR= colors.HexColor("#E8A000")
C_GREY = colors.HexColor("#555555")
C_WHITE = colors.white
C_BLACK = colors.black
C_FLOWBG = colors.HexColor("#EBF4FF")
def S(name,**kw): return ParagraphStyle(name,**kw)
TITLE = S("T", fontSize=15,leading=18,textColor=C_WHITE,fontName="Helvetica-Bold",alignment=TA_CENTER)
SUBT = S("ST", fontSize=8.5,leading=10,textColor=colors.HexColor("#A8C8F0"),fontName="Helvetica-Oblique",alignment=TA_CENTER)
H1 = S("H1", fontSize=11,leading=13,textColor=C_WHITE,fontName="Helvetica-Bold",alignment=TA_LEFT,spaceBefore=3,spaceAfter=2)
H2 = S("H2", fontSize=10,leading=12,textColor=C_MID,fontName="Helvetica-Bold",alignment=TA_LEFT,spaceBefore=4,spaceAfter=1)
BODY = S("BO", fontSize=9, leading=11.5,textColor=C_BLACK,fontName="Helvetica",alignment=TA_LEFT,spaceAfter=1)
BUL = S("BL", fontSize=9, leading=11.5,textColor=C_BLACK,fontName="Helvetica",alignment=TA_LEFT,leftIndent=10,bulletIndent=2,spaceAfter=1)
BUL2 = S("B2", fontSize=9, leading=11.5,textColor=C_BLACK,fontName="Helvetica",alignment=TA_LEFT,leftIndent=22,bulletIndent=14,spaceAfter=1)
CAP = S("CP", fontSize=8, leading=10,textColor=C_GREY,fontName="Helvetica-Oblique",alignment=TA_CENTER,spaceAfter=2)
CELL = S("CE", fontSize=8.5,leading=10.5,textColor=C_BLACK,fontName="Helvetica",alignment=TA_LEFT)
CELL_H = S("CH", fontSize=8.5,leading=10.5,textColor=C_WHITE,fontName="Helvetica-Bold",alignment=TA_CENTER)
FLOW_S = S("FS", fontSize=8.5,leading=10.5,textColor=C_DARK,fontName="Helvetica",alignment=TA_CENTER)
ARRS = S("AS", fontSize=12,leading=13,textColor=C_ACCENT,fontName="Helvetica-Bold",alignment=TA_CENTER)
def b(t): return f"<b>{t}</b>"
def i(t): return f"<i>{t}</i>"
def bi(t): return f"<b><i>{t}</i></b>"
def sec(title, emoji=""):
lbl = f"{emoji} {title}" if emoji else title
t = Table([[Paragraph(lbl,H1)]],colWidths=[CONTENT_W])
t.setStyle(TableStyle([
('BACKGROUND',(0,0),(-1,-1),C_MID),
('LEFTPADDING',(0,0),(-1,-1),6),('RIGHTPADDING',(0,0),(-1,-1),6),
('TOPPADDING',(0,0),(-1,-1),4),('BOTTOMPADDING',(0,0),(-1,-1),4),
]))
return t
def sub(t): return Paragraph(t,H2)
def bul(text,level=1):
st = BUL if level==1 else BUL2
ch = "•" if level==1 else "–"
return Paragraph(f"{ch} {text}",st)
def sp(h=4): return Spacer(1,h)
def tbl(headers,rows,widths=None,zebra=True):
if widths is None:
n=len(headers); widths=[CONTENT_W/n]*n
data=[[Paragraph(h,CELL_H) for h in headers]]
for row in rows:
data.append([Paragraph(str(c),CELL) for c in row])
ts=[
('BACKGROUND',(0,0),(-1,0),C_MID),('TEXTCOLOR',(0,0),(-1,0),C_WHITE),
('FONTNAME',(0,0),(-1,0),'Helvetica-Bold'),('FONTSIZE',(0,0),(-1,0),8.5),
('ALIGN',(0,0),(-1,0),'CENTER'),
('FONTNAME',(0,1),(-1,-1),'Helvetica'),('FONTSIZE',(0,1),(-1,-1),8.5),
('ALIGN',(0,1),(-1,-1),'LEFT'),
('GRID',(0,0),(-1,-1),0.5,colors.HexColor("#B0C8E8")),
('BOX',(0,0),(-1,-1),1,C_MID),
('LEFTPADDING',(0,0),(-1,-1),5),('RIGHTPADDING',(0,0),(-1,-1),5),
('TOPPADDING',(0,0),(-1,-1),3),('BOTTOMPADDING',(0,0),(-1,-1),3),
('VALIGN',(0,0),(-1,-1),'MIDDLE'),
]
if zebra:
for r in range(1,len(data)):
if r%2==0: ts.append(('BACKGROUND',(0,r),(-1,r),C_ROW))
t=Table(data,colWidths=widths)
t.setStyle(TableStyle(ts))
return t
def flowchart(steps):
items=[]
for idx,s in enumerate(steps):
items.append(s)
if idx<len(steps)-1: items.append("→")
n_s=len(steps); n_a=n_s-1
sw=(CONTENT_W-n_a*1.0*cm)/n_s
cw=[sw if idx%2==0 else 1.0*cm for idx in range(len(items))]
row=[]
for idx,item in enumerate(items):
if idx%2==0: row.append(Paragraph(item,FLOW_S))
else: row.append(Paragraph(item,ARRS))
t=Table([row],colWidths=cw)
ts=[
('VALIGN',(0,0),(-1,-1),'MIDDLE'),('ALIGN',(0,0),(-1,-1),'CENTER'),
('LEFTPADDING',(0,0),(-1,-1),4),('RIGHTPADDING',(0,0),(-1,-1),4),
('TOPPADDING',(0,0),(-1,-1),5),('BOTTOMPADDING',(0,0),(-1,-1),5),
]
for idx in range(0,len(items),2):
ts.append(('BACKGROUND',(idx,0),(idx,0),C_FLOWBG))
ts.append(('BOX',(idx,0),(idx,0),0.6,C_MID))
t.setStyle(TableStyle(ts))
return t
def note(text):
t=Table([[Paragraph(f"📌 {text}",S("NB",fontSize=8.5,leading=10.5,
fontName="Helvetica-Bold",textColor=C_DARK))]],colWidths=[CONTENT_W])
t.setStyle(TableStyle([
('BACKGROUND',(0,0),(-1,-1),C_NOTEBG),('BOX',(0,0),(-1,-1),0.8,C_NOTEBDR),
('LEFTPADDING',(0,0),(-1,-1),6),('RIGHTPADDING',(0,0),(-1,-1),6),
('TOPPADDING',(0,0),(-1,-1),4),('BOTTOMPADDING',(0,0),(-1,-1),4),
]))
return t
def fit_img(path,max_w,max_h):
from PIL import Image as P
pil=P.open(path)
w,h=pil.size
r=min(max_w/w,max_h/h)
return RLImage(path,width=w*r,height=h*r)
def side_by_side(left_items, right_img, right_w=6.5*cm):
lw = CONTENT_W - right_w - 0.3*cm
t = Table([[left_items, right_img]], colWidths=[lw, right_w])
t.setStyle(TableStyle([
('VALIGN',(0,0),(-1,-1),'MIDDLE'),
('LEFTPADDING',(0,0),(-1,-1),0),('RIGHTPADDING',(0,0),(-1,-1),0),
('TOPPADDING',(0,0),(-1,-1),0),('BOTTOMPADDING',(0,0),(-1,-1),0),
]))
return t
def footer(canvas, doc):
canvas.saveState()
canvas.setFont("Helvetica",7.5)
canvas.setFillColor(C_GREY)
canvas.drawCentredString(PAGE_W/2,BM*0.55,"Made By Sohaib Khan | SMC 5")
canvas.setStrokeColor(colors.HexColor("#CCCCCC"))
canvas.setLineWidth(0.5)
canvas.line(LM,BM*0.8,PAGE_W-RM,BM*0.8)
canvas.drawRightString(PAGE_W-RM,BM*0.55,f"Page {doc.page}")
canvas.restoreState()
# ─────────────────────────────────────────────────────────────────────────────
IMG = "/tmp/workspace/paget"
OUT = "/tmp/workspace/paget/PagetDisease_Notes.pdf"
doc = SimpleDocTemplate(OUT, pagesize=A4,
leftMargin=LM, rightMargin=RM,
topMargin=TM, bottomMargin=BM+0.8*cm,
title="Paget Disease of Bone – Structured Study Notes",
author="Sohaib Khan SMC 5")
story=[]
# ── TITLE ─────────────────────────────────────────────────────────────────────
ttbl=Table([
[Paragraph("🦴 PAGET DISEASE OF BONE",TITLE)],
[Paragraph("Osteitis Deformans | Structured Study Notes | Orthopaedics",SUBT)],
],colWidths=[CONTENT_W])
ttbl.setStyle(TableStyle([
('BACKGROUND',(0,0),(-1,-1),C_DARK),
('LEFTPADDING',(0,0),(-1,-1),8),('RIGHTPADDING',(0,0),(-1,-1),8),
('TOPPADDING',(0,0),(-1,-1),8),('BOTTOMPADDING',(0,0),(-1,-1),8),
]))
story.append(ttbl); story.append(sp(6))
# ══════════════════════════════════════════════════════════════════════════════
# SEC 1 – INTRODUCTION
# ══════════════════════════════════════════════════════════════════════════════
story.append(sec("1. INTRODUCTION & OVERVIEW","📖"))
story.append(sp(3))
story.append(sub("1a. Also Known As"))
story.append(bul(bi("Osteitis Deformans")))
story.append(bul("Named after Sir James Paget (1877)."))
story.append(sp(3))
story.append(sub("1b. Definition"))
story.append(bul("A " + bi("chronic, localized disorder of bone remodelling") + " in adults."))
story.append(bul("Characterised by " + b("accelerated, disorganised bone turnover") + " in one or more skeletal sites."))
story.append(bul("Results in " + b("enlarged, deformed, structurally weak") + " bone despite increased bone mass."))
story.append(sp(3))
story.append(sub("1c. Epidemiology"))
story.append(bul(b("Second most common") + " metabolic bone disease after osteoporosis."))
story.append(bul("Affects " + b("adults > 40 years") + "; rare below 40."))
story.append(bul(bi("Males > Females") + " (M:F = 2:1)."))
story.append(bul("More prevalent in " + i("UK, Australia, New Zealand, North America, Western Europe.") + " Rare in Asia, Africa."))
story.append(bul("Often " + b("asymptomatic") + " – discovered incidentally on X-ray or raised ALP."))
story.append(sp(6))
# ══════════════════════════════════════════════════════════════════════════════
# SEC 2 – AETIOLOGY
# ══════════════════════════════════════════════════════════════════════════════
story.append(sec("2. AETIOLOGY & GENETIC BASIS","🧬"))
story.append(sp(3))
story.append(sub("2a. Aetiology (Multifactorial)"))
story.append(bul(b("Genetic (hereditary) component:") + " ~15–30% have a " + bi("positive family history.")))
story.append(bul("Mutation in " + bi("SQSTM1 gene") + " (Sequestosome-1 / p62 protein) – most important identified mutation."))
story.append(bul("SQSTM1 encodes " + i("p62 protein") + " involved in the " + b("NF-κB signalling pathway") + " of osteoclasts."))
story.append(bul(b("Viral aetiology hypothesis:") + " Slow viral infection by " + bi("Paramyxovirus")
+ " (measles, respiratory syncytial virus, canine distemper virus) – controversial, not confirmed."))
story.append(bul("Inheritance: " + bi("Autosomal dominant") + " with variable penetrance."))
story.append(sp(6))
# ══════════════════════════════════════════════════════════════════════════════
# SEC 3 – PATHOPHYSIOLOGY
# ══════════════════════════════════════════════════════════════════════════════
story.append(sec("3. PATHOPHYSIOLOGY","🔬"))
story.append(sp(3))
story.append(sub("3a. Three Phases of Paget Disease"))
story.append(sp(2))
phase_h=["Phase","Key Process","Radiological Correlate"]
phase_r=[
[bi("Phase 1 – Osteolytic\n(Hot Phase)"),
b("Excessive osteoclastic resorption") + " – abnormally large, multinucleated osteoclasts with " + b(">20 nuclei per cell"),
bi("Osteoporosis circumscripta") + " (skull); " + bi("Blade of Grass / Flame sign") + " (long bones)"],
[bi("Phase 2 – Mixed\n(Active)"),
"Simultaneous " + b("osteoclast + osteoblast") + " activity; haphazard bone deposition",
b("Mixed lytic & sclerotic") + " pattern on X-ray"],
[bi("Phase 3 – Osteosclerotic\n(Burnt-Out / Cold)"),
b("Predominant osteoblast activity") + "; dense but structurally disorganised bone",
bi("Cotton wool appearance") + " (skull); " + bi("Picture-frame vertebra") + "; bone enlargement"],
]
story.append(tbl(phase_h,phase_r,widths=[3.8*cm,8*cm,CONTENT_W-11.8*cm]))
story.append(sp(4))
story.append(sub("3b. Pathophysiology Flowchart"))
story.append(sp(3))
story.append(flowchart([
"SQSTM1\nMutation\n(NF-κB ↑)",
"Abnormal Giant\nOsteoclast\nActivation",
"Excessive\nBone\nResorption",
"Disorganised\nOsteoblast\nResponse",
"Mosaic /\nWoven Bone\n(Structurally Weak)",
"Deformity,\nFractures,\nComplications",
]))
story.append(sp(4))
story.append(sub("3c. Key Histological Feature"))
story.append(bul(bi("Mosaic pattern") + " of bone: cement lines form a " + b("jigsaw puzzle") + " appearance due to repeated cycles of resorption and deposition."))
story.append(bul("Abnormal osteoclasts: " + b("very large, multinucleated") + " (up to 100 nuclei), compared to normal 3–5 nuclei."))
story.append(bul("Woven bone replaces lamellar bone → " + b("structurally inferior") + " bone despite increased mass."))
story.append(sp(6))
# ══════════════════════════════════════════════════════════════════════════════
# SEC 4 – BONES COMMONLY INVOLVED
# ══════════════════════════════════════════════════════════════════════════════
story.append(sec("4. BONES COMMONLY INVOLVED","🦴"))
story.append(sp(4))
bones_h=["Bone / Site","Frequency","Key Clinical / Radiological Sign"]
bones_r=[
[bi("Pelvis"), b("Most common") + " (~70%)", "Enlarged, sclerotic pelvis; picture-frame ilium"],
[bi("Lumbar Spine"), "~50%", bi("Picture-frame vertebra") + "; enlarged vertebral body"],
[bi("Skull"), "~25–40%", bi("Cotton wool") + " appearance; " + i("osteoporosis circumscripta") + " (early); frontal bossing; hearing loss"],
[bi("Femur"), "~25%", bi("Bowing") + " – lateral bowing; blade of grass sign"],
[bi("Tibia"), "~20%", bi("Anterior bowing") + " (sabre tibia); medial tibial stress fractures"],
[i("Humerus, clavicle"), "Less common", "Enlargement, cortical thickening"],
]
story.append(tbl(bones_h,bones_r,widths=[4*cm,3*cm,CONTENT_W-7*cm]))
story.append(sp(6))
# ══════════════════════════════════════════════════════════════════════════════
# SEC 5 – CLINICAL FEATURES
# ══════════════════════════════════════════════════════════════════════════════
story.append(sec("5. CLINICAL FEATURES","🩺"))
story.append(sp(3))
story.append(sub("5a. Symptoms"))
story.append(bul(b("Often asymptomatic") + " – incidental discovery (raised ALP, abnormal X-ray)."))
story.append(bul(bi("Bone pain:") + " Deep, aching, " + b("worse at rest and at night") + " (unlike OA which improves at rest)."))
story.append(bul(bi("Bone deformity:") + " Bowing of long bones, skull enlargement, spinal kyphosis."))
story.append(bul(bi("Pathological fractures:") + " Due to structurally weak bone – " + b("chalk-stick / banana fractures") + " in long bones."))
story.append(bul("Fractures " + b("most common in femur") + " > tibia > humerus."))
story.append(sp(3))
story.append(sub("5b. Skull Involvement"))
img1 = fit_img(os.path.join(IMG,"img1_cotton_wool.jpg"), max_w=7*cm, max_h=5.5*cm)
skull_left = [
bul(bi("Frontal bossing") + " – enlargement of the frontal bone."),
sp(2),
bul(b("Increased hat size") + " – classical history."),
sp(2),
bul(bi("Hearing loss") + " (most common neurological complication):"),
bul(b("Conductive:") + " narrowing of external auditory canal / ossicular involvement.", level=2),
bul(b("Sensorineural:") + " 8th cranial nerve compression at skull base.", level=2),
sp(2),
bul(bi("Basilar invagination") + " (platybasia) – softened skull base telescopes into cranium → " + b("brain stem compression.")),
sp(2),
bul(bi("Lion face") + " (leontiasis ossea) – severe facial involvement."),
]
story.append(side_by_side(skull_left, img1, right_w=7.2*cm))
story.append(Paragraph("<i>Fig 1: Cotton wool appearance on skull X-ray – areas of osteosclerosis on dense skull (Paget disease)</i>", CAP))
story.append(sp(4))
story.append(sub("5c. Long Bone & Spine Involvement"))
img2 = fit_img(os.path.join(IMG,"img2_tibia_xray.jpg"), max_w=5*cm, max_h=6.5*cm)
img3 = fit_img(os.path.join(IMG,"img3_flame_sign.jpg"), max_w=5*cm, max_h=5*cm)
bone_left = [
bul(bi("Anterior bowing of tibia") + " (" + i("sabre tibia") + ") – hallmark of tibial Paget."),
sp(2),
bul(bi("Lateral bowing of femur") + "."),
sp(2),
bul(bi("Blade of grass / Flame sign") + " on X-ray:"),
bul("V-shaped lytic advancing front in long bone diaphysis.", level=2),
bul("Seen in the " + b("osteolytic phase."), level=2),
sp(2),
bul(bi("Picture-frame vertebra") + " – cortical thickening of all 4 vertebral margins with central radiolucency."),
sp(2),
bul(bi("Ivory vertebra") + " – uniformly dense vertebral body (sclerotic phase)."),
]
imgs_row = Table([[img2, sp(4), img3]], colWidths=[5.2*cm, 0.5*cm, 5.2*cm])
imgs_row.setStyle(TableStyle([
('VALIGN',(0,0),(-1,-1),'MIDDLE'),
('LEFTPADDING',(0,0),(-1,-1),0),('RIGHTPADDING',(0,0),(-1,-1),0),
('TOPPADDING',(0,0),(-1,-1),0),('BOTTOMPADDING',(0,0),(-1,-1),0),
]))
rw = CONTENT_W - 5.2*cm - 5.2*cm - 0.5*cm - 0.4*cm
bone_layout = Table([[bone_left, imgs_row]], colWidths=[rw, 10.9*cm])
bone_layout.setStyle(TableStyle([
('VALIGN',(0,0),(-1,-1),'MIDDLE'),
('LEFTPADDING',(0,0),(-1,-1),0),('RIGHTPADDING',(0,0),(-1,-1),0),
('TOPPADDING',(0,0),(-1,-1),0),('BOTTOMPADDING',(0,0),(-1,-1),0),
]))
story.append(bone_layout)
story.append(Paragraph(
"<i>Fig 2: Bowing tibia X-ray (Paget) | Fig 3: Blade of grass / Flame sign – V-shaped lytic front in long bone</i>", CAP))
story.append(sp(4))
story.append(sub("5d. Vascular & Other Manifestations"))
story.append(bul(bi("High-output cardiac failure:") + " Markedly " + b("increased blood flow") + " to pagetic bone (hypervascular) → "
+ b("arteriovenous shunting") + " → increased cardiac output → high-output CCF."))
story.append(bul("Skin over pagetic bone may be " + b("warm") + " (increased vascularity)."))
story.append(bul(bi("Spinal stenosis:") + " Enlarged vertebrae compress spinal cord and nerve roots."))
story.append(bul(bi("Cranial nerve palsies:") + " CN VIII (most common), CN II, CN VII due to foraminal narrowing."))
story.append(sp(6))
# ══════════════════════════════════════════════════════════════════════════════
# SEC 6 – NON-ORTHOPAEDIC MANIFESTATIONS TABLE
# ══════════════════════════════════════════════════════════════════════════════
story.append(sec("6. SYSTEMIC MANIFESTATIONS","⚕️"))
story.append(sp(3))
sys_h=["System","Manifestation","Mechanism"]
sys_r=[
[b("Cardiovascular"),
bi("High-output cardiac failure"),
"Hypervascular pagetic bone → AV shunting → ↑ cardiac output"],
[b("Neurological"),
bi("Hearing loss") + " (CN VIII most common)",
"Conductive: canal narrowing; Sensorineural: CN VIII compression"],
["",
"Cranial nerve palsies (CN II, VII, VIII)",
"Foraminal narrowing by sclerotic bone"],
["",
bi("Basilar invagination") + " → brainstem compression",
"Softened skull base collapses inward"],
["",
i("Spinal stenosis, radiculopathy, myelopathy"),
"Enlarged vertebrae compress neural structures"],
[b("Oncological"),
bi("Osteosarcoma") + " (most feared complication)",
"Malignant transformation of pagetic bone (~1% lifetime risk); " + b("ALP markedly elevated")],
["",
i("Giant cell tumour"),
"Less common malignant transformation"],
[b("Metabolic"),
i("Hypercalcaemia") + " (if immobilised)",
"Immobility + high bone turnover releases Ca²⁺"],
["",
i("Hyperuricaemia / gout"),
"Increased nucleic acid turnover from high cell activity"],
]
story.append(tbl(sys_h,sys_r,widths=[3*cm,5.5*cm,CONTENT_W-8.5*cm]))
story.append(sp(6))
# ══════════════════════════════════════════════════════════════════════════════
# SEC 7 – INVESTIGATIONS
# ══════════════════════════════════════════════════════════════════════════════
story.append(sec("7. INVESTIGATIONS & DIAGNOSIS","🔎"))
story.append(sp(3))
story.append(sub("7a. Biochemical Markers"))
story.append(sp(2))
bio_h=["Marker","Finding in Paget","Notes"]
bio_r=[
[bi("Serum Alkaline Phosphatase (ALP)"),
b("Markedly elevated") + " (best single marker)",
b("Most reliable") + " marker for diagnosis and monitoring therapy"],
[i("Urinary hydroxyproline"),
b("Elevated") + " (reflects bone resorption)",
"Less specific; now largely replaced by newer markers"],
[i("Serum osteocalcin"),
"Elevated",
"Marker of osteoblast activity"],
[i("Urinary N-telopeptide / CTX"),
"Elevated",
"Specific bone resorption markers"],
[i("Serum calcium & phosphate"),
b("Usually normal"),
"Raised Ca²⁺ only with immobilisation"],
[i("PTH"),
"Normal",
"Distinguishes from hyperparathyroidism"],
]
story.append(tbl(bio_h,bio_r,widths=[5*cm,5*cm,CONTENT_W-10*cm]))
story.append(sp(4))
story.append(sub("7b. Radiological Features Summary"))
story.append(sp(2))
rad_h=["Sign / Feature","Description","Phase / Site"]
rad_r=[
[bi("Cotton wool appearance"),
"Patchy osteosclerosis with ill-defined densities",
b("Sclerotic phase") + " – Skull"],
[bi("Osteoporosis circumscripta"),
"Well-defined osteolysis of the skull",
b("Osteolytic phase") + " – Skull"],
[bi("Blade of grass / Flame sign"),
"V-shaped lytic advancing front from articular end",
b("Osteolytic phase") + " – Long bones"],
[bi("Picture-frame vertebra"),
"Cortical thickening of all 4 vertebral borders",
"Mixed/sclerotic – Spine"],
[bi("Ivory vertebra"),
"Uniformly dense vertebral body",
"Sclerotic phase – Spine"],
[bi("Bowing of long bones"),
"Anterior (tibia), lateral (femur)",
"Advanced disease"],
[bi("Enlarged bone / cortical thickening"),
"Bone wider than normal, cortex thickened",
"All phases"],
[bi("Chalk-stick / Banana fracture"),
"Transverse fracture of bowed long bone; incomplete fissure fractures on convex cortex",
"Pathological – Long bones"],
]
story.append(tbl(rad_h,rad_r,widths=[5*cm,6.5*cm,CONTENT_W-11.5*cm]))
story.append(sp(4))
story.append(sub("7c. Bone Scan (Isotope Scintigraphy)"))
story.append(bul(b("Best investigation to assess") + " the " + bi("extent") + " and " + bi("distribution") + " of pagetic involvement across the whole skeleton."))
story.append(bul("Shows " + b("hot spots") + " (increased uptake) at sites of active disease."))
story.append(bul("Used for " + b("staging") + " and monitoring treatment response."))
story.append(sp(4))
story.append(sub("7d. Histological Findings"))
story.append(bul(bi("Mosaic pattern") + " of cement lines – pathognomonic of Paget disease."))
story.append(bul("Abnormally " + b("large multinucleated osteoclasts") + " (up to 100 nuclei)."))
story.append(bul(b("Woven bone") + " replacing normal lamellar bone."))
story.append(sp(6))
# Mosaic histology image
img4 = fit_img(os.path.join(IMG,"img4_mosaic.jpg"), max_w=6.5*cm, max_h=7*cm)
hist_left=[
Paragraph(b("Histological Appearance:"), S("HL",fontSize=9,leading=11,fontName="Helvetica-Bold",textColor=C_MID)),
sp(3),
bul(bi("Mosaic bone pattern") + " with irregular cement lines resembling a jigsaw puzzle."),
sp(2),
bul("Reflects " + b("repeated episodes") + " of disorganised bone resorption and deposition."),
sp(2),
bul("Giant " + b("multinucleated osteoclasts") + " with up to 100 nuclei."),
sp(2),
bul(b("Increased vascularity") + " of bone marrow."),
]
hist_layout=Table([[hist_left,img4]],colWidths=[CONTENT_W-7*cm,7*cm])
hist_layout.setStyle(TableStyle([
('VALIGN',(0,0),(-1,-1),'MIDDLE'),
('LEFTPADDING',(0,0),(-1,-1),0),('RIGHTPADDING',(0,0),(-1,-1),0),
('TOPPADDING',(0,0),(-1,-1),0),('BOTTOMPADDING',(0,0),(-1,-1),0),
]))
story.append(hist_layout)
story.append(Paragraph("<i>Fig 4: Mosaic / jigsaw cement line pattern on histology – pathognomonic of Paget disease</i>",CAP))
story.append(sp(6))
# ══════════════════════════════════════════════════════════════════════════════
# SEC 8 – TREATMENT
# ══════════════════════════════════════════════════════════════════════════════
story.append(sec("8. TREATMENT MODALITIES","💊"))
story.append(sp(4))
story.append(sub("8a. Indications for Treatment"))
story.append(bul("Bone pain attributable to Paget disease."))
story.append(bul("Preparation for " + b("orthopaedic surgery") + " on pagetic bone (reduces vascularity and blood loss)."))
story.append(bul("Neurological complications (hearing loss, spinal stenosis)."))
story.append(bul("Hypercalcaemia due to immobilisation."))
story.append(bul("Skull, spine, or weight-bearing bone involvement to prevent deformity/fracture."))
story.append(bul(b("Asymptomatic disease:") + " treatment may be considered if ALP markedly elevated or bone at risk."))
story.append(sp(3))
tx_h=["Treatment","Mechanism / Indication","Notes"]
tx_r=[
[bi("Bisphosphonates\n(Drug of Choice)"),
b("Inhibit osteoclast activity") + " → reduce excessive bone resorption → normalise ALP",
bi("Zoledronic acid") + " (single IV infusion) = " + b("most potent & preferred") + "; "
+ i("Pamidronate") + " (IV), " + i("Alendronate, Risedronate") + " (oral)"],
[bi("Calcitonin"),
"Inhibits osteoclasts; alternative if bisphosphonates contraindicated or poorly tolerated",
b("Salmon calcitonin") + " used; SC/IM injection; largely replaced by bisphosphonates"],
[bi("Mithramycin\n(Plicamycin)"),
"Cytotoxic antibiotic; inhibits osteoclast RNA synthesis",
"Reserved for " + b("severe, refractory cases") + " or malignant transformation; significant toxicity"],
[b("NSAIDs / Analgesics"),
"Symptomatic pain relief",
"Adjunct therapy only"],
[b("Surgical Management"),
"Fracture fixation; joint replacement (hip/knee); spinal decompression; osteotomy for bowing",
b("Preoperative bisphosphonates") + " given to reduce pagetic vascularity and operative blood loss"],
]
story.append(tbl(tx_h,tx_r,widths=[4*cm,6.5*cm,CONTENT_W-10.5*cm]))
story.append(sp(6))
# ══════════════════════════════════════════════════════════════════════════════
# SEC 9 – COMPLICATIONS
# ══════════════════════════════════════════════════════════════════════════════
story.append(sec("9. COMPLICATIONS","⚠️"))
story.append(sp(4))
comp_h=["Complication","Details","Key Feature"]
comp_r=[
[bi("Osteosarcoma"),
"Most feared; ~1% lifetime risk; suspect if " + b("sudden worsening of pain") + " + markedly ↑ ALP",
b("Poor prognosis") + "; elderly onset sarcoma"],
[bi("Pathological fractures"),
b("Chalk-stick / banana fractures") + " in weight-bearing bones",
"Femur most common"],
[bi("High-output cardiac failure"),
"Hypervascular pagetic bone → AV shunting → ↑ cardiac output",
"Especially with > 30% skeletal involvement"],
[bi("Basilar invagination"),
"Softened skull base telescopes into cranium",
"Brainstem + lower CN compression"],
[bi("Deafness"),
b("CN VIII compression") + " or ossicular involvement",
"Most common neurological complication"],
[bi("Spinal stenosis"),
"Enlarged vertebrae → canal narrowing",
"Myelopathy / radiculopathy"],
[i("Hypercalcaemia"),
"During immobilisation",
"Treat underlying disease + hydration"],
[i("Hyperuricaemia / Gout"),
"Increased cell turnover → uric acid ↑",
"Manage with allopurinol"],
]
story.append(tbl(comp_h,comp_r,widths=[4*cm,7.5*cm,CONTENT_W-11.5*cm]))
story.append(sp(6))
# ══════════════════════════════════════════════════════════════════════════════
# SEC 10 – SUMMARY
# ══════════════════════════════════════════════════════════════════════════════
story.append(sec("10. KEY SUMMARY","📋"))
story.append(sp(4))
sum_h=["Aspect","Details"]
sum_r=[
[b("Synonyms"), bi("Osteitis Deformans")],
[b("Definition"), "Chronic localised disorder of " + bi("accelerated, disorganised bone remodelling") + " in adults"],
[b("Aetiology"), bi("SQSTM1 gene mutation") + " (NF-κB pathway); viral hypothesis (" + i("Paramyxovirus") + ")"],
[b("Inheritance"), bi("Autosomal dominant") + " (variable penetrance); sporadic cases common"],
[b("Epidemiology"), "Adults > 40 yrs; " + b("M > F (2:1)") + "; commonest in UK/Australia"],
[b("Phase 1 (Lytic)"), bi("Osteoclast predominant") + "; " + i("osteoporosis circumscripta") + " (skull); " + i("blade of grass") + " sign (long bone)"],
[b("Phase 2 (Mixed)"), "Osteoclast + osteoblast activity; mixed lytic-sclerotic"],
[b("Phase 3 (Sclerotic)"), bi("Osteoblast predominant") + "; " + i("cotton wool") + " (skull); " + i("picture-frame vertebra") + "; " + i("ivory vertebra")],
[b("Histology"), bi("Mosaic pattern") + " – cement lines; giant multinucleated osteoclasts (up to 100 nuclei)"],
[b("Best Marker"), bi("Serum ALP") + " (markedly elevated; best for diagnosis + monitoring)"],
[b("Extent Assessment"), bi("Bone scan") + " (isotope scintigraphy) – whole-body distribution"],
[b("Drug of Choice"), bi("Bisphosphonates") + " – " + bi("Zoledronic acid") + " (single IV infusion, most potent)"],
[b("Feared Complication"), bi("Osteosarcoma") + " (~1%); suspect with sudden pain + markedly ↑ ALP"],
[b("Most Common Comp."), bi("Hearing loss") + " (CN VIII – conductive or sensorineural)"],
[b("Bones Involved"), "Pelvis (most common) > Lumbar spine > Skull > Femur > Tibia"],
]
story.append(tbl(sum_h,sum_r,widths=[4.5*cm,CONTENT_W-4.5*cm]))
story.append(sp(5))
story.append(note(
"Drug of choice: Zoledronic acid (bisphosphonate) | "
"Best marker: Serum ALP | "
"Best for extent: Bone scan | "
"Most feared complication: Osteosarcoma | "
"Most common complication: Hearing loss (CN VIII) | "
"Pathognomonic histology: Mosaic / jigsaw cement line pattern | "
"Radiology: Cotton wool (skull), Blade of grass (long bone), Picture-frame (vertebra)"
))
doc.build(story, onFirstPage=footer, onLaterPages=footer)
print(f"PDF written: {OUT}")
cd /tmp/workspace/paget && python3 build_paget_pdf.py 2>&1
python3 -c " from pypdf import PdfReader; import os r = PdfReader('/tmp/workspace/paget/PagetDisease_Notes.pdf') print(f'Pages: {len(r.pages)}') print(f'Size: {os.path.getsize(\"/tmp/workspace/paget/PagetDisease_Notes.pdf\")/1024:.1f} KB') "
PagetDisease Notes
PDF Document · PDF