paediatric distal radius fractures
"distal radius fracture"[MeSH Terms] AND child[MeSH Terms]
paediatric distal radius fractures management classification 2024 guidelines
"radius fracture" AND "children" AND "treatment"
https://www.ncbi.nlm.nih.gov/books/NBK536916
paediatric distal radius fracture acceptable angulation remodeling potential K-wire indications 2024
https://www.sicot-j.org/articles/sicotj/full_html/2026/01/si…
| Type | Description | Stability |
|---|---|---|
| Torus (buckle) | Axial compression buckles the metaphyseal cortex; cortical continuity preserved | Stable |
| Greenstick | Tension-side cortex fails; compression-side intact; may have angulation | Partially stable |
| Complete | Both cortices disrupted; may be displaced, translated, or bayoneted | Unstable |
| Plastic deformation | Bone bends without discrete fracture line (more common in ulna) | Stable |
| Type | Pattern | Frequency at Distal Radius | Growth Disturbance Risk |
|---|---|---|---|
| SH I | Through physis only | Common | Rare |
| SH II | Through physis + metaphysis (Thurston-Holland fragment) | Most common | Rare |
| SH III | Through physis + epiphysis (intra-articular) | Rare | Medium |
| SH IV | Through metaphysis + physis + epiphysis | Rare | High |
| SH V | Crush injury of physis | Very rare | High |
| Parameter | Under 10 years (or girls ≤8, boys ≤10) | Over 10 years (or girls >8, boys >10) |
|---|---|---|
| Sagittal angulation | ≤20-30° | ≤15° |
| Coronal angulation | ≤10° | ≤10° |
| Rotation | ≤15° | ≤30° (functional) |
| Bayonet apposition | Up to 1 cm acceptable in <11 years | Up to 1 cm |
| Technique | Indication |
|---|---|
| Percutaneous K-wire (transphyseal or intrafocal/Kapandji) | Unstable metaphyseal or SH II fractures; first-line surgical option |
| Intrafocal (Kapandji) K-wire | Preferred for SH II - avoids transphyseal pin damage to growth plate |
| Elastic intramedullary nails (TENS/ESIN) | Unstable diaphyseal/distal shaft fractures; higher morbidity than K-wires |
| Open reduction + internal fixation (plate/screw) | SH III/IV intra-articular fractures; irreducible fractures; near-skeletal maturity |
| Complication | Comment |
|---|---|
| Redisplacement | Most common complication; 20-35% in casted metaphyseal fractures |
| Malunion | Usually well-tolerated in young children due to remodeling; significant in older patients |
| Growth disturbance/growth arrest | Risk increases with SH grade; also increased with delayed reduction (>3 days), repeat reduction attempts |
| Refracture | More common after removal of immobilisation; protective splinting during high-risk activities |
| Median/anterior interosseous nerve injury | Usually neuropraxia; resolves spontaneously |
| DRUJ instability | Associated with Galeazzi-equivalent injuries in children |
| Compartment syndrome | Rare but must be excluded in displaced fractures |
| Physeal arrest | Late complication of SH V or undertreated physeal injuries; may cause progressive deformity |
Create a quick reference PDF on paediatric distal radius fracture reduction parameters and management algorithm
ls -la /home/daytona/workspace/ 2>/dev/null || echo "workspace empty or does not exist"
mkdir -p /home/daytona/workspace/paeds-radius-pdf && python3 -c "import reportlab; print('reportlab ok')" && python3 -c "from reportlab.lib import colors; print('colors ok')"
~/paeds-radius-pdf/build_pdf.py
"""
Paediatric Distal Radius Fracture - Quick Reference PDF
Uses ReportLab to produce a clean, colour-coded 2-page reference card.
"""
from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.units import mm, cm
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.platypus import (
SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
HRFlowable, KeepTogether
)
from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_RIGHT
from reportlab.platypus import Flowable
from reportlab.pdfbase import pdfmetrics
OUTPUT = "/home/daytona/workspace/paeds-radius-pdf/Paeds_Distal_Radius_QR.pdf"
# ── Colour palette ─────────────────────────────────────────────────────────────
NAVY = colors.HexColor("#1A3557")
TEAL = colors.HexColor("#1B7A8C")
ORANGE = colors.HexColor("#E85D04")
GREEN = colors.HexColor("#2D6A4F")
RED = colors.HexColor("#C1121F")
AMBER = colors.HexColor("#E9A900")
LTBLUE = colors.HexColor("#D6EAF8")
LTTEAL = colors.HexColor("#D0EEF2")
LTGREEN = colors.HexColor("#D8F3DC")
LTORANGE = colors.HexColor("#FFF0E0")
LTRED = colors.HexColor("#FDECEA")
LTAMBER = colors.HexColor("#FFF8E1")
VLTGREY = colors.HexColor("#F7F9FB")
MIDGREY = colors.HexColor("#95A5A6")
WHITE = colors.white
BLACK = colors.black
# ── Custom horizontal rule ─────────────────────────────────────────────────────
class ColoredHR(Flowable):
def __init__(self, width, color, thickness=0.8):
Flowable.__init__(self)
self.width = width
self.color = color
self.thickness = thickness
self.height = thickness + 1
def draw(self):
self.canv.setStrokeColor(self.color)
self.canv.setLineWidth(self.thickness)
self.canv.line(0, 0, self.width, 0)
# ── Section header helper ──────────────────────────────────────────────────────
def section_header(text, color=NAVY, bg=LTBLUE, width=170*mm):
style = ParagraphStyle(
"SH", fontName="Helvetica-Bold", fontSize=9.5,
textColor=WHITE, leading=14, leftIndent=5, spaceBefore=2
)
data = [[Paragraph(text, style)]]
t = Table(data, colWidths=[width])
t.setStyle(TableStyle([
("BACKGROUND", (0, 0), (-1, -1), color),
("TOPPADDING", (0, 0), (-1, -1), 4),
("BOTTOMPADDING", (0, 0), (-1, -1), 4),
("LEFTPADDING", (0, 0), (-1, -1), 8),
("RIGHTPADDING", (0, 0), (-1, -1), 8),
]))
return t
# ── Body paragraph style ───────────────────────────────────────────────────────
BODY = ParagraphStyle("Body", fontName="Helvetica", fontSize=8, leading=11,
textColor=BLACK)
BODYB = ParagraphStyle("BodyB", fontName="Helvetica-Bold", fontSize=8, leading=11,
textColor=BLACK)
SMALL = ParagraphStyle("Small", fontName="Helvetica", fontSize=7, leading=10,
textColor=BLACK)
SMALLB = ParagraphStyle("SmallB", fontName="Helvetica-Bold", fontSize=7, leading=10,
textColor=BLACK)
FOOTNOTE = ParagraphStyle("Footnote", fontName="Helvetica-Oblique", fontSize=6.5,
leading=9, textColor=MIDGREY)
TITLE_S = ParagraphStyle("TitleS", fontName="Helvetica-Bold", fontSize=16,
leading=20, textColor=WHITE, alignment=TA_CENTER)
SUBTITLE_S = ParagraphStyle("SubS", fontName="Helvetica", fontSize=9,
leading=13, textColor=LTBLUE, alignment=TA_CENTER)
HEADER_S = ParagraphStyle("HS", fontName="Helvetica-Bold", fontSize=8.5,
leading=12, textColor=NAVY)
WARN = ParagraphStyle("Warn", fontName="Helvetica-BoldOblique", fontSize=7.5,
leading=11, textColor=RED)
def sp(n=3):
return Spacer(1, n)
# ──────────────────────────────────────────────────────────────────────────────
# BUILD DOCUMENT
# ──────────────────────────────────────────────────────────────────────────────
doc = SimpleDocTemplate(
OUTPUT, pagesize=A4,
leftMargin=18*mm, rightMargin=18*mm,
topMargin=14*mm, bottomMargin=14*mm,
title="Paediatric Distal Radius Fracture Quick Reference",
author="Orris Medical"
)
W = A4[0] - 36*mm # usable width
story = []
# ══════════════════════════════════════════════════════════════════════════════
# TITLE BANNER
# ══════════════════════════════════════════════════════════════════════════════
title_data = [[
Paragraph("Paediatric Distal Radius Fracture", TITLE_S),
Paragraph("Quick Reference Card", TITLE_S),
]]
title_table = Table([[
Paragraph("Paediatric Distal Radius Fracture", TITLE_S),
]], colWidths=[W])
title_table.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), NAVY),
("TOPPADDING", (0,0), (-1,-1), 10),
("BOTTOMPADDING", (0,0), (-1,-1), 4),
]))
story.append(title_table)
sub_table = Table([[
Paragraph("Reduction Parameters | Management Algorithm | Salter-Harris Guide", SUBTITLE_S),
]], colWidths=[W])
sub_table.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), TEAL),
("TOPPADDING", (0,0), (-1,-1), 4),
("BOTTOMPADDING", (0,0), (-1,-1), 6),
]))
story.append(sub_table)
story.append(sp(6))
# ══════════════════════════════════════════════════════════════════════════════
# ROW 1: Left = Fracture Types | Right = Key Principles
# ══════════════════════════════════════════════════════════════════════════════
CW = (W - 4*mm) / 2 # half-width columns with small gap
# ── LEFT: Fracture Types ──────────────────────────────────────────────────────
ft_header = section_header("FRACTURE TYPES", TEAL, LTTEAL, CW)
ft_data = [
[Paragraph("<b>Type</b>", SMALL), Paragraph("<b>Description</b>", SMALL),
Paragraph("<b>Stability</b>", SMALL)],
[Paragraph("Torus\n(Buckle)", SMALL),
Paragraph("Axial compression; cortical buckling; continuity preserved", SMALL),
Paragraph("Stable", SMALL)],
[Paragraph("Greenstick", SMALL),
Paragraph("Tension cortex fails; compression side intact; angulation", SMALL),
Paragraph("Partial", SMALL)],
[Paragraph("Complete", SMALL),
Paragraph("Both cortices disrupted; may be displaced / bayoneted", SMALL),
Paragraph("Unstable", SMALL)],
[Paragraph("Plastic\nDeform.", SMALL),
Paragraph("Bone bows without discrete fracture line", SMALL),
Paragraph("Stable", SMALL)],
]
ft_table = Table(ft_data, colWidths=[CW*0.22, CW*0.55, CW*0.23])
ft_table.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,0), TEAL),
("TEXTCOLOR", (0,0), (-1,0), WHITE),
("ROWBACKGROUNDS", (0,1), (-1,-1), [WHITE, LTTEAL]),
("FONTNAME", (0,0), (-1,0), "Helvetica-Bold"),
("FONTSIZE", (0,0), (-1,-1), 7),
("TOPPADDING", (0,0), (-1,-1), 3),
("BOTTOMPADDING", (0,0), (-1,-1), 3),
("LEFTPADDING", (0,0), (-1,-1), 4),
("RIGHTPADDING", (0,0), (-1,-1), 3),
("GRID", (0,0), (-1,-1), 0.3, colors.HexColor("#BDC3C7")),
("VALIGN", (0,0), (-1,-1), "MIDDLE"),
]))
# ── RIGHT: Key Principles ─────────────────────────────────────────────────────
kp_header = section_header("KEY PAEDIATRIC PRINCIPLES", NAVY, LTBLUE, CW)
kp_bullets = [
("Distal radial physis", "contributes 75% of radial longitudinal growth → huge remodeling potential"),
("Remodeling inversely", "correlates with age — greatest in young children"),
("Sagittal plane", "corrects best (plane of wrist motion); rotational deformity does NOT remodel"),
("One reduction attempt", "only for physeal injuries — repeat attempts increase growth arrest risk"),
("Bayonet apposition", "acceptable ≤1 cm in children under 11 years"),
("Torus fractures", "are stable — no reduction needed; splint 3-4 weeks only"),
("Galeazzi equivalent", "in children = distal radius fracture + SH I/II distal ulna (not ligamentous)"),
]
kp_rows = [[
Paragraph(f"<b>{a}</b>", SMALL),
Paragraph(b, SMALL)
] for a, b in kp_bullets]
kp_table = Table(kp_rows, colWidths=[CW*0.38, CW*0.62])
kp_table.setStyle(TableStyle([
("ROWBACKGROUNDS", (0,0), (-1,-1), [WHITE, LTBLUE]),
("TOPPADDING", (0,0), (-1,-1), 3),
("BOTTOMPADDING", (0,0), (-1,-1), 3),
("LEFTPADDING", (0,0), (-1,-1), 4),
("RIGHTPADDING", (0,0), (-1,-1), 3),
("VALIGN", (0,0), (-1,-1), "TOP"),
("GRID", (0,0), (-1,-1), 0.3, colors.HexColor("#BDC3C7")),
]))
row1 = Table(
[[ft_header, "", kp_header],
[ft_table, "", kp_table]],
colWidths=[CW, 4*mm, CW]
)
row1.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), 2),
]))
story.append(row1)
story.append(sp(6))
# ══════════════════════════════════════════════════════════════════════════════
# REDUCTION PARAMETERS TABLE (full width)
# ══════════════════════════════════════════════════════════════════════════════
story.append(section_header("ACCEPTABLE REDUCTION PARAMETERS (Noonan & Price Criteria)", ORANGE, LTORANGE, W))
story.append(sp(2))
rp_header_row = [
Paragraph("<b>Parameter</b>", SMALLB),
Paragraph("<b>Age <5 yrs</b>", SMALLB),
Paragraph("<b>Age 5-10 yrs\n(Girls ≤8 / Boys ≤10)</b>", SMALLB),
Paragraph("<b>Age >10 yrs\n(Girls >8 / Boys >10)</b>", SMALLB),
Paragraph("<b>Near Skeletal\nMaturity</b>", SMALLB),
]
rp_rows = [
["Sagittal (AP) angulation", "≤35°", "≤25°", "≤15°", "≤10° (anatomic)"],
["Coronal angulation", "≤15°", "≤10°", "≤10°", "≤5°"],
["Rotation", "≤15°", "≤15°", "≤30°", "Anatomic"],
["Bayonet apposition", "≤1 cm", "≤1 cm", "≤1 cm", "Not acceptable"],
["Angulation in distal shaft >10°", "—", "Causes ~20° loss pronation-supination", "→ Reduces function", "Correct"],
]
rp_data = [rp_header_row] + [
[Paragraph(row[0], SMALL)] + [Paragraph(v, SMALL) for v in row[1:]]
for row in rp_rows
]
rp_col_w = [W*0.30, W*0.13, W*0.19, W*0.19, W*0.19]
rp_table = Table(rp_data, colWidths=rp_col_w)
rp_table.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,0), ORANGE),
("TEXTCOLOR", (0,0), (-1,0), WHITE),
("ROWBACKGROUNDS", (0,1), (-1,-1), [WHITE, LTORANGE]),
("FONTSIZE", (0,0), (-1,-1), 7),
("TOPPADDING", (0,0), (-1,-1), 3),
("BOTTOMPADDING", (0,0), (-1,-1), 3),
("LEFTPADDING", (0,0), (-1,-1), 5),
("RIGHTPADDING", (0,0), (-1,-1), 4),
("GRID", (0,0), (-1,-1), 0.3, colors.HexColor("#CCCCCC")),
("VALIGN", (0,0), (-1,-1), "MIDDLE"),
("ALIGN", (1,0), (-1,-1), "CENTER"),
]))
story.append(rp_table)
story.append(sp(6))
# ══════════════════════════════════════════════════════════════════════════════
# ROW 2: Salter-Harris | Greenstick Tolerances
# ══════════════════════════════════════════════════════════════════════════════
# ── LEFT: Salter-Harris Guide ─────────────────────────────────────────────────
sh_header = section_header("SALTER-HARRIS PHYSEAL INJURIES", RED, LTRED, CW)
sh_data = [
[Paragraph("<b>Type</b>", SMALLB),
Paragraph("<b>Pattern</b>", SMALLB),
Paragraph("<b>Freq.</b>", SMALLB),
Paragraph("<b>Growth Risk</b>", SMALLB),
Paragraph("<b>Management</b>", SMALLB)],
["I", "Through physis only", "Common", "Rare", "CR + BEP 4 wks"],
["II", "Physis + metaphysis (Thurston-Holland)", "Most common", "Rare", "CR + BEP 4 wks;\nintrafocal K-wire if unstable"],
["III","Physis + epiphysis (intra-art.)", "Rare", "Medium", "ORIF - refer ortho"],
["IV", "Physis + metaphysis + epiphysis", "Rare", "HIGH", "ORIF - refer ortho"],
["V", "Crush injury of physis", "Very rare", "VERY HIGH","Often missed;\ngrowth arrest late"],
]
sh_fmt = [
[Paragraph(row[0], SMALLB if i == 0 else SMALL),
Paragraph(row[1], SMALL),
Paragraph(row[2], SMALL),
Paragraph(row[3], SMALLB if row[3] in ("HIGH","VERY HIGH") else SMALL),
Paragraph(row[4], SMALL)]
for i, row in enumerate(sh_data)
]
sh_col_w = [CW*0.07, CW*0.35, CW*0.15, CW*0.16, CW*0.27]
sh_table = Table(sh_fmt, colWidths=sh_col_w)
sh_style = [
("BACKGROUND", (0,0), (-1,0), RED),
("TEXTCOLOR", (0,0), (-1,0), WHITE),
("ROWBACKGROUNDS", (0,1), (-1,-1), [WHITE, LTRED]),
("FONTSIZE", (0,0), (-1,-1), 7),
("TOPPADDING", (0,0), (-1,-1), 3),
("BOTTOMPADDING", (0,0), (-1,-1), 3),
("LEFTPADDING", (0,0), (-1,-1), 4),
("RIGHTPADDING", (0,0), (-1,-1), 3),
("GRID", (0,0), (-1,-1), 0.3, colors.HexColor("#CCCCCC")),
("VALIGN", (0,0), (-1,-1), "TOP"),
]
# Colour HIGH/VERY HIGH cells
for row_idx, row in enumerate(sh_data[1:], start=1):
if row[3] in ("HIGH", "VERY HIGH"):
sh_style.append(("TEXTCOLOR", (3, row_idx), (3, row_idx), RED))
sh_table.setStyle(TableStyle(sh_style))
# ── RIGHT: Greenstick + Torus management ─────────────────────────────────────
gs_header = section_header("METAPHYSEAL FRACTURE MANAGEMENT", GREEN, LTGREEN, CW)
gs_data = [
[Paragraph("<b>Fracture</b>", SMALLB),
Paragraph("<b>Management</b>", SMALLB),
Paragraph("<b>Cast</b>", SMALLB),
Paragraph("<b>Duration</b>", SMALLB)],
[Paragraph("Torus\n(Buckle)", SMALL),
Paragraph("Splint only\nNo reduction", SMALL),
Paragraph("Removable\nor soft cast", SMALL),
Paragraph("3-4 wks", SMALL)],
[Paragraph("Greenstick\n(undisplaced)", SMALL),
Paragraph("Cast without\nreduction", SMALL),
Paragraph("Below-elbow", SMALL),
Paragraph("4-6 wks", SMALL)],
[Paragraph("Greenstick\n(angulated)", SMALL),
Paragraph("CR if exceeds\nage limits", SMALL),
Paragraph("Below-elbow\nmoulded cast", SMALL),
Paragraph("4-6 wks", SMALL)],
[Paragraph("Complete\n(non-displaced)", SMALL),
Paragraph("Cast; no\nreduction", SMALL),
Paragraph("Below-elbow", SMALL),
Paragraph("4-6 wks", SMALL)],
[Paragraph("Complete\n(displaced)", SMALL),
Paragraph("CR under\nsedation/GA", SMALL),
Paragraph("Moulded\nBE or AE cast", SMALL),
Paragraph("4-6 wks", SMALL)],
[Paragraph("High-risk\ncomplete", SMALL),
Paragraph("CR + K-wire\npercutaneous", SMALL),
Paragraph("Above-elbow\ncast", SMALL),
Paragraph("4-5 wks\n(then remove)", SMALL)],
]
gs_col_w = [CW*0.25, CW*0.30, CW*0.25, CW*0.20]
gs_table = Table(gs_data, colWidths=gs_col_w)
gs_table.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,0), GREEN),
("TEXTCOLOR", (0,0), (-1,0), WHITE),
("ROWBACKGROUNDS", (0,1), (-1,-1), [WHITE, LTGREEN]),
("FONTSIZE", (0,0), (-1,-1), 7),
("TOPPADDING", (0,0), (-1,-1), 3),
("BOTTOMPADDING", (0,0), (-1,-1), 3),
("LEFTPADDING", (0,0), (-1,-1), 4),
("RIGHTPADDING", (0,0), (-1,-1), 3),
("GRID", (0,0), (-1,-1), 0.3, colors.HexColor("#CCCCCC")),
("VALIGN", (0,0), (-1,-1), "TOP"),
]))
row2 = Table(
[[sh_header, "", gs_header],
[sh_table, "", gs_table]],
colWidths=[CW, 4*mm, CW]
)
row2.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), 2),
]))
story.append(row2)
story.append(sp(6))
# ══════════════════════════════════════════════════════════════════════════════
# MANAGEMENT ALGORITHM (full width)
# ══════════════════════════════════════════════════════════════════════════════
story.append(section_header("MANAGEMENT ALGORITHM", NAVY, LTBLUE, W))
story.append(sp(2))
# Two-column algorithm: Metaphyseal pathway | Physeal pathway
algo_title_style = ParagraphStyle("AT", fontName="Helvetica-Bold", fontSize=8,
textColor=WHITE, alignment=TA_CENTER, leading=12)
algo_body_style = ParagraphStyle("AB", fontName="Helvetica", fontSize=7.5,
textColor=BLACK, leading=11, leftIndent=4)
algo_bold_style = ParagraphStyle("ABB", fontName="Helvetica-Bold", fontSize=7.5,
textColor=NAVY, leading=11, leftIndent=4)
HCW = (W - 4*mm) / 2
# Metaphyseal
meta_steps = [
("DISTAL RADIUS FRACTURE", NAVY),
("Is it a PHYSEAL injury?", ORANGE),
("NO → Metaphyseal Pathway", TEAL),
("Torus/Buckle\n→ Splint 3-4 wks; no reduction; discharge", GREEN),
("Greenstick/Complete\n→ Assess angulation vs. age-appropriate limits", TEAL),
("Within limits?\n→ Closed reduction under sedation\nMoulded below-elbow cast 4-6 wks", TEAL),
("High-Risk Features?\n• Age >10 yrs\n• Initial displacement >50%\n• Bayonet apposition (>11 yrs)\n• Poor cast index (>0.7)\n• Failed reduction", ORANGE),
("YES → Percutaneous K-wire fixation\n+ above-elbow cast 4-5 wks", RED),
("Follow-up X-ray\nat 1 week\n(peak redisplacement)", NAVY),
]
phy_steps = [
("YES → Physeal Pathway", RED),
("Salter-Harris I or II", TEAL),
("Undisplaced\n→ BEP or removable splint 4 wks\nFracture clinic within 5 days", GREEN),
("Displaced\n→ ONE attempt closed reduction\n + BEP cast 4 wks\n Refer if unable to reduce", AMBER),
("⚠ Do NOT reduce if >5 days old", RED),
("⚠ ONE attempt ONLY — repeat\n attempts → growth arrest risk", RED),
("Unstable SH II\n→ Intrafocal (Kapandji) K-wire\n Avoids transphyseal injury", ORANGE),
("Salter-Harris III / IV\n→ Intra-articular\n→ REFER ORTHOPAEDICS\n→ Usually ORIF required", RED),
("Salter-Harris V\n→ Often missed acutely\n→ Growth arrest on follow-up\n→ Refer orthopaedics", RED),
]
def algo_box(steps, col_color):
rows = []
for text, color in steps:
style = ParagraphStyle(
"BS", fontName="Helvetica-Bold" if color in (RED, ORANGE) else "Helvetica",
fontSize=7, textColor=WHITE if color not in (AMBER,) else BLACK,
leading=10, leftIndent=4, rightIndent=4, alignment=TA_LEFT
)
cell = Table([[Paragraph(text, style)]], colWidths=[HCW - 2*mm])
cell.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), color),
("TOPPADDING", (0,0), (-1,-1), 4),
("BOTTOMPADDING", (0,0), (-1,-1), 4),
("LEFTPADDING", (0,0), (-1,-1), 6),
("RIGHTPADDING", (0,0), (-1,-1), 4),
("ROUNDEDCORNERS", [3]),
]))
rows.append([cell])
rows.append([Spacer(1, 2)])
outer = Table(rows, colWidths=[HCW])
outer.setStyle(TableStyle([
("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 outer
meta_col = algo_box(meta_steps, TEAL)
phy_col = algo_box(phy_steps, RED)
algo_label_meta = Table([[Paragraph("METAPHYSEAL PATHWAY", algo_title_style)]], colWidths=[HCW])
algo_label_meta.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), TEAL),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING", (0,0), (-1,-1), 5),
]))
algo_label_phy = Table([[Paragraph("PHYSEAL PATHWAY", algo_title_style)]], colWidths=[HCW])
algo_label_phy.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), RED),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING", (0,0), (-1,-1), 5),
]))
algo_outer = Table(
[[algo_label_meta, Spacer(4*mm, 1), algo_label_phy],
[meta_col, Spacer(4*mm, 1), phy_col]],
colWidths=[HCW, 4*mm, HCW]
)
algo_outer.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(algo_outer)
story.append(sp(6))
# ══════════════════════════════════════════════════════════════════════════════
# ROW 3: Surgical Options | Complications
# ══════════════════════════════════════════════════════════════════════════════
# ── LEFT: Surgical options ────────────────────────────────────────────────────
so_header = section_header("SURGICAL OPTIONS", AMBER, LTAMBER, CW)
so_data = [
[Paragraph("<b>Technique</b>", SMALLB), Paragraph("<b>Indication</b>", SMALLB)],
[Paragraph("Percutaneous K-wire\n(transphyseal)", SMALL),
Paragraph("Unstable metaphyseal complete fractures;\nfirst-line surgical option", SMALL)],
[Paragraph("Intrafocal K-wire\n(Kapandji)", SMALL),
Paragraph("Unstable SH II — avoids transphyseal pin;\npreserves growth plate", SMALL)],
[Paragraph("Elastic intramedullary\nnails (TENS/ESIN)", SMALL),
Paragraph("Unstable distal shaft / diaphyseal;\nhigher morbidity than K-wires", SMALL)],
[Paragraph("ORIF (plate/screw)", SMALL),
Paragraph("SH III/IV intra-articular; irreducible fractures;\nnear-skeletal maturity", SMALL)],
]
so_col_w = [CW*0.40, CW*0.60]
so_table = Table(so_data, colWidths=so_col_w)
so_table.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,0), colors.HexColor("#B7850A")),
("TEXTCOLOR", (0,0), (-1,0), WHITE),
("ROWBACKGROUNDS", (0,1), (-1,-1), [WHITE, LTAMBER]),
("FONTSIZE", (0,0), (-1,-1), 7),
("TOPPADDING", (0,0), (-1,-1), 3),
("BOTTOMPADDING", (0,0), (-1,-1), 3),
("LEFTPADDING", (0,0), (-1,-1), 4),
("RIGHTPADDING", (0,0), (-1,-1), 3),
("GRID", (0,0), (-1,-1), 0.3, colors.HexColor("#CCCCCC")),
("VALIGN", (0,0), (-1,-1), "TOP"),
]))
# ── RIGHT: Complications ──────────────────────────────────────────────────────
cx_header = section_header("COMPLICATIONS", RED, LTRED, CW)
cx_data = [
[Paragraph("<b>Complication</b>", SMALLB), Paragraph("<b>Key Points</b>", SMALLB)],
[Paragraph("Redisplacement", SMALL),
Paragraph("20-35% after cast alone; 0-5% after K-wire;\npeak at 1-2 weeks - mandatory X-ray review", SMALL)],
[Paragraph("Malunion", SMALL),
Paragraph("Often tolerated in young children (remodels);\nsignificant in older patients/near-skeletal maturity", SMALL)],
[Paragraph("Growth disturbance", SMALL),
Paragraph("Risk correlates with SH grade; also increased\nby delayed reduction (>3 days) or repeat attempts", SMALL)],
[Paragraph("Nerve injury\n(median/AIN)", SMALL),
Paragraph("Usually neuropraxia; resolves spontaneously;\nassess motor/sensory at presentation", SMALL)],
[Paragraph("Compartment\nsyndrome", SMALL),
Paragraph("Rare but exclude in displaced fractures;\n5 Ps — urgent fasciotomy if suspected", SMALL)],
[Paragraph("Refracture", SMALL),
Paragraph("Protective splinting for high-risk activities\nafter cast removal", SMALL)],
]
cx_col_w = [CW*0.32, CW*0.68]
cx_table = Table(cx_data, colWidths=cx_col_w)
cx_table.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,0), RED),
("TEXTCOLOR", (0,0), (-1,0), WHITE),
("ROWBACKGROUNDS", (0,1), (-1,-1), [WHITE, LTRED]),
("FONTSIZE", (0,0), (-1,-1), 7),
("TOPPADDING", (0,0), (-1,-1), 3),
("BOTTOMPADDING", (0,0), (-1,-1), 3),
("LEFTPADDING", (0,0), (-1,-1), 4),
("RIGHTPADDING", (0,0), (-1,-1), 3),
("GRID", (0,0), (-1,-1), 0.3, colors.HexColor("#CCCCCC")),
("VALIGN", (0,0), (-1,-1), "TOP"),
]))
row3 = Table(
[[so_header, "", cx_header],
[so_table, "", cx_table]],
colWidths=[CW, 4*mm, CW]
)
row3.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), 2),
]))
story.append(row3)
story.append(sp(6))
# ══════════════════════════════════════════════════════════════════════════════
# SURGICAL REFERRAL INDICATIONS (full width, red border box)
# ══════════════════════════════════════════════════════════════════════════════
story.append(section_header("INDICATIONS FOR URGENT ORTHOPAEDIC REFERRAL", RED, LTRED, W))
story.append(sp(2))
referral_items = [
"Open fracture",
"Neurovascular compromise (pulseless limb → vascular surgery urgently)",
"Compartment syndrome",
"Failed or irreducible closed reduction",
"Salter-Harris III, IV, or V injuries",
"Intra-articular fracture",
"Significantly displaced fractures in skeletally mature adolescents",
"Suspected child abuse (non-accidental injury) pattern",
]
ref_cols = [referral_items[:4], referral_items[4:]]
ref_left = "\n".join(f"• {x}" for x in ref_cols[0])
ref_right = "\n".join(f"• {x}" for x in ref_cols[1])
ref_style = ParagraphStyle("Ref", fontName="Helvetica", fontSize=7.5,
leading=12, textColor=BLACK)
ref_table = Table(
[[Paragraph(ref_left, ref_style), Paragraph(ref_right, ref_style)]],
colWidths=[W*0.5, W*0.5]
)
ref_table.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), LTRED),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING", (0,0), (-1,-1), 5),
("LEFTPADDING", (0,0), (-1,-1), 8),
("RIGHTPADDING", (0,0), (-1,-1), 8),
("LINEBELOW", (0,0), (-1,-1), 0.5, RED),
("BOX", (0,0), (-1,-1), 1, RED),
("VALIGN", (0,0), (-1,-1), "TOP"),
]))
story.append(ref_table)
story.append(sp(5))
# ══════════════════════════════════════════════════════════════════════════════
# FOOTER
# ══════════════════════════════════════════════════════════════════════════════
story.append(ColoredHR(W, MIDGREY, 0.5))
story.append(sp(3))
footer_data = [
[Paragraph("References: Noonan & Price criteria | SICOT-J 2026 systematic review (45 studies, 5,340 pts) | StatPearls Distal Radius Fractures | RCH Melbourne Clinical Guidelines | PMC10364841", FOOTNOTE),
Paragraph("Orris Medical | June 2026\nFor educational use only. Verify against local guidelines.", FOOTNOTE)]
]
footer_table = Table(footer_data, colWidths=[W*0.70, W*0.30])
footer_table.setStyle(TableStyle([
("VALIGN", (0,0), (-1,-1), "TOP"),
("ALIGN", (1,0), (1,0), "RIGHT"),
("TOPPADDING", (0,0), (-1,-1), 0),
("BOTTOMPADDING", (0,0), (-1,-1), 0),
("LEFTPADDING", (0,0), (-1,-1), 0),
("RIGHTPADDING", (0,0), (-1,-1), 0),
]))
story.append(footer_table)
# ── BUILD ────────────────────────────────────────────────────────────────────
doc.build(story)
print(f"PDF saved to: {OUTPUT}")
cd /home/daytona/workspace/paeds-radius-pdf && python3 build_pdf.py 2>&1
~/paeds-radius-pdf/build_pdf.py
Successfully replaced 1 instance(s) of the string in '/home/daytona/workspace/paeds-radius-pdf/build_pdf.py'
~/paeds-radius-pdf/build_pdf.py
Successfully replaced 1 instance(s) of the string in '/home/daytona/workspace/paeds-radius-pdf/build_pdf.py'
cd /home/daytona/workspace/paeds-radius-pdf && python3 build_pdf.py 2>&1
ls -lh /home/daytona/workspace/paeds-radius-pdf/Paeds_Distal_Radius_QR.pdf
python3 -c " from pypdf import PdfReader r = PdfReader('/home/daytona/workspace/paeds-radius-pdf/Paeds_Distal_Radius_QR.pdf') print(f'Pages: {len(r.pages)}') for i, p in enumerate(r.pages): txt = p.extract_text() print(f'Page {i+1} chars: {len(txt)}') print(txt[:200]) print('---') "
Paeds Distal Radius QR
PDF Document · PDF