My mother is suffering from frozen shoulder suggest it's treatment
"frozen shoulder" OR "adhesive capsulitis" AND treatment
frozen shoulder adhesive capsulitis treatment guidelines 2024 2025
| Stage | What's happening | Duration | Main symptom |
|---|---|---|---|
| 1. Freezing (Painful) | Inflammation, severe pain with any movement | Weeks to months | Pain dominates |
| 2. Frozen (Stiffening) | Adhesions form, capsule contracts, pain lessens | 4-12 months | Stiffness dominates |
| 3. Thawing (Resolution) | Motion gradually returns | Months to years | Gradual improvement |
| Priority | Treatment | When to use |
|---|---|---|
| First | NSAIDs + physiotherapy + heat | All stages |
| Second | Corticosteroid injection | Painful/freezing phase |
| Third | Hydrodilatation (distension injection) | Frozen/stiffening phase |
| Fourth | Manual therapy + structured PT | Frozen phase |
| Fifth | Shockwave therapy | Adjunct if needed |
| Sixth | MUA or arthroscopic release | If >6-12 months, no improvement |
Create a PDF guide with home exercises and stretches she can print
ls -la /tmp/workspace/ 2>/dev/null || echo "workspace empty or missing"
mkdir -p /tmp/workspace/frozen-shoulder && python3 -c "from reportlab.pdfgen import canvas; print('reportlab ok')"
~/frozen-shoulder/generate_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_CENTER, TA_LEFT, TA_JUSTIFY
from reportlab.platypus import (
SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
HRFlowable, KeepTogether
)
from reportlab.lib.colors import HexColor
OUTPUT = "/tmp/workspace/frozen-shoulder/Frozen_Shoulder_Exercise_Guide.pdf"
# ── Colours ────────────────────────────────────────────────────────────────
DARK_BLUE = HexColor("#1A3A5C")
MED_BLUE = HexColor("#2E6DA4")
LIGHT_BLUE = HexColor("#D6E8F7")
ACCENT = HexColor("#E8A020")
LIGHT_GREY = HexColor("#F5F5F5")
MID_GREY = HexColor("#CCCCCC")
TEXT_DARK = HexColor("#1C1C1C")
GREEN = HexColor("#2E7D32")
RED = HexColor("#C62828")
WHITE = colors.white
# ── Document ───────────────────────────────────────────────────────────────
doc = SimpleDocTemplate(
OUTPUT,
pagesize=A4,
rightMargin=1.8*cm, leftMargin=1.8*cm,
topMargin=2*cm, bottomMargin=2*cm,
title="Frozen Shoulder Home Exercise Guide",
author="Orris Medical"
)
W, H = A4
CONTENT_W = W - 3.6*cm
# ── Styles ─────────────────────────────────────────────────────────────────
styles = getSampleStyleSheet()
def style(name, **kw):
return ParagraphStyle(name, **kw)
S_TITLE = style("MainTitle",
fontName="Helvetica-Bold", fontSize=22,
textColor=WHITE, alignment=TA_CENTER, leading=28,
spaceAfter=4)
S_SUBTITLE = style("SubTitle",
fontName="Helvetica", fontSize=11,
textColor=LIGHT_BLUE, alignment=TA_CENTER, leading=16)
S_SECTION = style("Section",
fontName="Helvetica-Bold", fontSize=13,
textColor=WHITE, alignment=TA_LEFT, leading=18,
spaceBefore=4, spaceAfter=4)
S_EXERCISE_TITLE = style("ExTitle",
fontName="Helvetica-Bold", fontSize=11,
textColor=DARK_BLUE, leading=14, spaceBefore=2)
S_BODY = style("Body",
fontName="Helvetica", fontSize=9.5,
textColor=TEXT_DARK, leading=14, alignment=TA_JUSTIFY)
S_BULLET = style("Bullet",
fontName="Helvetica", fontSize=9.5,
textColor=TEXT_DARK, leading=13,
leftIndent=12, firstLineIndent=0)
S_WARNING = style("Warning",
fontName="Helvetica-Bold", fontSize=9,
textColor=RED, leading=13)
S_TIP = style("Tip",
fontName="Helvetica-Oblique", fontSize=9,
textColor=GREEN, leading=13)
S_NOTE = style("Note",
fontName="Helvetica", fontSize=8.5,
textColor=HexColor("#555555"), leading=12, alignment=TA_CENTER)
S_TABLE_HDR = style("TblHdr",
fontName="Helvetica-Bold", fontSize=9,
textColor=WHITE, alignment=TA_CENTER, leading=12)
S_TABLE_CELL = style("TblCell",
fontName="Helvetica", fontSize=9,
textColor=TEXT_DARK, leading=12)
S_SMALL_BOLD = style("SmBold",
fontName="Helvetica-Bold", fontSize=8.5,
textColor=DARK_BLUE, leading=12)
story = []
# ══════════════════════════════════════════════════════════════════════════
# HEADER BANNER (title block as a table)
# ══════════════════════════════════════════════════════════════════════════
header_data = [[
Paragraph("Frozen Shoulder", S_TITLE),
], [
Paragraph("Home Exercise & Stretch Guide", S_TITLE),
], [
Paragraph("A printable daily programme for recovery from adhesive capsulitis", S_SUBTITLE),
]]
header_table = Table(header_data, colWidths=[CONTENT_W])
header_table.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), DARK_BLUE),
("TOPPADDING", (0,0), (-1,0), 16),
("BOTTOMPADDING",(0,-1),(-1,-1), 14),
("LEFTPADDING", (0,0), (-1,-1), 10),
("RIGHTPADDING",(0,0), (-1,-1), 10),
("ROWBACKGROUNDS",(0,0),(-1,-1),[DARK_BLUE]),
]))
story.append(header_table)
story.append(Spacer(1, 10))
# ══════════════════════════════════════════════════════════════════════════
# IMPORTANT NOTICE BOX
# ══════════════════════════════════════════════════════════════════════════
notice_data = [[
Paragraph(
"<b>Before You Start:</b> Always warm the shoulder with a heat pack or warm shower for "
"5-10 minutes before exercising. Work within a <b>pain-free or mild-discomfort range</b> only. "
"Stop immediately if you feel sharp pain. Do each session <b>2-3 times daily.</b>",
style("NoticeText", fontName="Helvetica", fontSize=9.5,
textColor=HexColor("#7B3F00"), leading=14)
)
]]
notice_table = Table(notice_data, colWidths=[CONTENT_W])
notice_table.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), HexColor("#FFF3CD")),
("BOX", (0,0), (-1,-1), 1.2, HexColor("#E8A020")),
("TOPPADDING", (0,0), (-1,-1), 8),
("BOTTOMPADDING",(0,0),(-1,-1), 8),
("LEFTPADDING", (0,0), (-1,-1), 10),
("RIGHTPADDING",(0,0), (-1,-1), 10),
]))
story.append(notice_table)
story.append(Spacer(1, 12))
# ══════════════════════════════════════════════════════════════════════════
# HELPER: section header
# ══════════════════════════════════════════════════════════════════════════
def section_header(text, color=MED_BLUE):
data = [[Paragraph(text, S_SECTION)]]
t = Table(data, colWidths=[CONTENT_W])
t.setStyle(TableStyle([
("BACKGROUND", (0,0),(-1,-1), color),
("TOPPADDING", (0,0),(-1,-1), 6),
("BOTTOMPADDING",(0,0),(-1,-1), 6),
("LEFTPADDING", (0,0),(-1,-1), 10),
]))
return t
# ── Helper: exercise card ──────────────────────────────────────────────────
def exercise_card(number, name, reps, duration, how_to, tip=None, warning=None):
"""Returns a KeepTogether block for one exercise."""
items = []
# Title row: number badge + name + reps
badge_data = [[
Paragraph(f"<b>{number}</b>",
style("Badge", fontName="Helvetica-Bold", fontSize=12,
textColor=WHITE, alignment=TA_CENTER, leading=14)),
Paragraph(f"<b>{name}</b>",
style("EName", fontName="Helvetica-Bold", fontSize=11,
textColor=DARK_BLUE, leading=14)),
Paragraph(f"{reps}<br/><font size='8' color='#555555'>{duration}</font>",
style("Reps", fontName="Helvetica-Bold", fontSize=10,
textColor=MED_BLUE, alignment=TA_CENTER, leading=13)),
]]
badge_table = Table(badge_data, colWidths=[1.2*cm, CONTENT_W - 4.5*cm, 3.3*cm])
badge_table.setStyle(TableStyle([
("BACKGROUND", (0,0),(0,0), MED_BLUE),
("BACKGROUND", (1,0),(2,0), LIGHT_BLUE),
("VALIGN", (0,0),(-1,-1), "MIDDLE"),
("TOPPADDING", (0,0),(-1,-1), 6),
("BOTTOMPADDING",(0,0),(-1,-1), 6),
("LEFTPADDING", (0,0),(0,0), 4),
("RIGHTPADDING",(2,0),(2,0), 8),
("LEFTPADDING", (1,0),(1,0), 8),
]))
items.append(badge_table)
# Steps box
steps_lines = [Paragraph(f"<bullet>•</bullet> {step}", S_BULLET)
for step in how_to]
inner = [Spacer(1,4)] + steps_lines + [Spacer(1,4)]
if tip:
inner.append(Paragraph(f"<b>Tip:</b> {tip}", S_TIP))
if warning:
inner.append(Paragraph(f"<b>Caution:</b> {warning}", S_WARNING))
inner.append(Spacer(1,2))
steps_data = [[inner_item] for inner_item in inner]
steps_table = Table([[inner]], colWidths=[CONTENT_W])
steps_table.setStyle(TableStyle([
("BACKGROUND", (0,0),(-1,-1), LIGHT_GREY),
("BOX", (0,0),(-1,-1), 0.5, MID_GREY),
("TOPPADDING", (0,0),(-1,-1), 2),
("BOTTOMPADDING",(0,0),(-1,-1), 4),
("LEFTPADDING", (0,0),(-1,-1), 10),
("RIGHTPADDING",(0,0),(-1,-1), 10),
]))
items.append(steps_table)
items.append(Spacer(1, 8))
return KeepTogether(items)
# ══════════════════════════════════════════════════════════════════════════
# SECTION 1 - WARM-UP
# ══════════════════════════════════════════════════════════════════════════
story.append(section_header("WARM-UP (Do this before every session)"))
story.append(Spacer(1, 6))
story.append(exercise_card(
"W", "Heat Application", "5-10 min", "Once before session",
[
"Apply a warm heat pack or hot water bottle to the shoulder.",
"Alternatively, stand under a warm shower and let the water run over the shoulder.",
"This loosens the joint and reduces pain before stretching.",
],
tip="Never apply heat directly to bare skin - wrap in a thin towel."
))
story.append(exercise_card(
"W2", "Neck & Upper Trap Release", "5 slow rolls each side", "60 seconds",
[
"Sit upright in a chair. Gently drop your ear toward your shoulder.",
"Hold 10 seconds, then slowly roll your chin toward your chest.",
"Roll to the other side and hold 10 seconds.",
"This relaxes the muscles around the shoulder girdle.",
],
tip="Keep the shoulders relaxed and low - do not shrug."
))
story.append(Spacer(1, 4))
# ══════════════════════════════════════════════════════════════════════════
# SECTION 2 - RANGE OF MOTION EXERCISES
# ══════════════════════════════════════════════════════════════════════════
story.append(section_header("RANGE OF MOTION EXERCISES"))
story.append(Spacer(1, 6))
story.append(exercise_card(
"1", "Pendulum Swings", "10 circles each direction", "2 min",
[
"Stand beside a table or chair and lean forward, resting your good arm on the surface.",
"Let the affected arm hang down loosely - completely relaxed.",
"Gently swing the arm in small clockwise circles (10 times), then counter-clockwise (10 times).",
"Let gravity do the work - do not actively move the shoulder muscles.",
"You can also swing forward/backward and side-to-side (10 reps each).",
],
tip="The smaller the circle, the better to start. Gradually increase size over weeks.",
warning="Do not swing forcefully. This is a passive, gentle movement only."
))
story.append(exercise_card(
"2", "Wall Finger Walk (Forward)", "10 steps up, 2-3 sets", "2 min",
[
"Stand facing a wall, about arm's length away.",
"Place the fingertips of your affected hand on the wall at waist height.",
"Walk your fingers slowly up the wall as high as comfortable, without raising your shoulder.",
"Hold at the top for 5 seconds, then slowly walk fingers back down.",
"Mark your highest point each day to track progress.",
],
tip="Stand closer to the wall as flexibility improves to increase the stretch.",
))
story.append(exercise_card(
"3", "Wall Finger Walk (Side)", "10 steps up, 2-3 sets", "2 min",
[
"Stand with your affected side facing the wall, about arm's length away.",
"Place fingertips on the wall at hip level.",
"Walk fingers sideways and upward as high as comfortable.",
"Hold 5 seconds at the top, then walk slowly back down.",
],
tip="This targets abduction (side lifting) which is usually the most restricted direction.",
))
story.append(exercise_card(
"4", "Pendulum Horizontal Swing", "20 swings", "1-2 min",
[
"In the same leaning position as Exercise 1 (supported by good arm).",
"Swing the affected arm gently forward and backward like a pendulum.",
"Then swing side to side (across your body and outward).",
"Keep the movement smooth and rhythmic.",
],
))
# ══════════════════════════════════════════════════════════════════════════
# SECTION 3 - STRETCHES
# ══════════════════════════════════════════════════════════════════════════
story.append(section_header("STRETCHING EXERCISES (Hold each stretch gently)"))
story.append(Spacer(1, 6))
story.append(exercise_card(
"5", "Cross-Body (Horizontal Adduction) Stretch", "3 x 30 sec holds", "2-3 min",
[
"Sit or stand upright.",
"Use your good hand to gently grasp the elbow of the affected arm.",
"Slowly pull the affected arm across your chest toward the opposite shoulder.",
"Hold for 30 seconds. You should feel a gentle stretch in the back of the shoulder.",
"Release slowly. Repeat 3 times.",
],
tip="Only pull until you feel a mild stretch - never force it.",
warning="Stop if you feel sharp pain radiating down the arm."
))
story.append(exercise_card(
"6", "External Rotation Stretch (Doorway)", "3 x 20-30 sec holds", "2 min",
[
"Stand in a doorway with your elbow bent at 90 degrees.",
"Place your forearm against the door frame vertically.",
"Gently lean your body forward slightly until you feel a stretch at the front of the shoulder.",
"Hold 20-30 seconds. Repeat 3 times.",
"Alternatively: Lie on your back, elbow at 90 degrees - gently lower the back of your hand toward the floor.",
],
tip="External rotation is the most important direction to regain - prioritise this stretch.",
))
story.append(exercise_card(
"7", "Towel Internal Rotation Stretch", "3 x 20 sec holds", "2 min",
[
"Hold a bath towel with both hands behind your back - good arm on top, affected arm below.",
"Use the good arm to gently pull the towel upward, lifting the affected arm slightly.",
"Hold for 20 seconds. You will feel a stretch at the shoulder and upper arm.",
"Slowly lower and repeat 3 times.",
],
tip="Do not jerk or pull sharply - smooth, sustained stretch only.",
warning="If you cannot reach behind your back, skip this exercise until mobility improves."
))
story.append(exercise_card(
"8", "Overhead Reach (Supine)", "10 reps, 2 sets", "2 min",
[
"Lie on your back on a firm surface (bed or floor with a yoga mat).",
"Clasp both hands together, interlinking fingers.",
"Use the good arm to slowly assist the affected arm to raise both arms up overhead.",
"Go as far as comfortable. Hold 5 seconds. Slowly lower back down.",
],
tip="Gravity assists the stretch in this position - much easier than standing.",
))
# ══════════════════════════════════════════════════════════════════════════
# SECTION 4 - STRENGTHENING (Phase 2/3 only)
# ══════════════════════════════════════════════════════════════════════════
story.append(section_header("GENTLE STRENGTHENING (Begin only when pain is manageable)", color=HexColor("#5C3A1A")))
story.append(Spacer(1, 6))
story.append(Paragraph(
"Only start these when you can move the arm with mild discomfort only (not sharp pain). "
"These help restore muscle strength and prevent weakness from prolonged reduced use.",
S_BODY))
story.append(Spacer(1, 6))
story.append(exercise_card(
"9", "Isometric External Rotation", "10 x 5 sec holds, 2 sets", "2 min",
[
"Stand sideways next to a wall, affected arm closest to the wall.",
"Bend the elbow at 90 degrees, keep the elbow close to your side.",
"Press the back of your hand gently into the wall (as if trying to rotate outward).",
"Hold 5 seconds. Release. Repeat 10 times.",
"No movement occurs - this is an isometric (static) exercise.",
],
tip="Isometric exercises build strength without moving the joint - perfect for painful phases.",
))
story.append(exercise_card(
"10", "Isometric Abduction", "10 x 5 sec holds, 2 sets", "2 min",
[
"Stand with your affected arm resting by your side.",
"Press the outside of your arm gently against the wall or a door frame.",
"Push outward (as if lifting the arm sideways) but keep the arm still.",
"Hold 5 seconds. Release. Repeat 10 times.",
],
))
story.append(exercise_card(
"11", "Scapular Retraction (Shoulder Blade Squeeze)", "15 reps, 3 sets", "2 min",
[
"Sit or stand upright with arms relaxed at sides.",
"Gently squeeze your shoulder blades together as if trying to hold a pencil between them.",
"Hold 5 seconds. Slowly release.",
"This strengthens the postural muscles around the shoulder blade.",
],
tip="Keep shoulders relaxed (not hunched up) and breathe normally throughout.",
))
# ══════════════════════════════════════════════════════════════════════════
# SECTION 5 - DAILY SCHEDULE
# ══════════════════════════════════════════════════════════════════════════
story.append(Spacer(1, 4))
story.append(section_header("RECOMMENDED DAILY SCHEDULE"))
story.append(Spacer(1, 8))
schedule_data = [
[Paragraph("<b>Session</b>", S_TABLE_HDR),
Paragraph("<b>Exercises</b>", S_TABLE_HDR),
Paragraph("<b>Time</b>", S_TABLE_HDR)],
[Paragraph("Morning (after warm shower)", S_TABLE_CELL),
Paragraph("Warm-up + Exercises 1, 2, 5, 6", S_TABLE_CELL),
Paragraph("~15 min", S_TABLE_CELL)],
[Paragraph("Afternoon", S_TABLE_CELL),
Paragraph("Exercises 3, 4, 7, 8", S_TABLE_CELL),
Paragraph("~10 min", S_TABLE_CELL)],
[Paragraph("Evening (before bed)", S_TABLE_CELL),
Paragraph("Exercises 5, 6, 9, 10, 11 (if pain allows)", S_TABLE_CELL),
Paragraph("~10 min", S_TABLE_CELL)],
]
sched_col_w = [CONTENT_W*0.38, CONTENT_W*0.42, CONTENT_W*0.20]
sched_table = Table(schedule_data, colWidths=sched_col_w)
sched_table.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,0), MED_BLUE),
("ROWBACKGROUNDS",(0,1), (-1,-1), [WHITE, LIGHT_BLUE]),
("BOX", (0,0), (-1,-1), 1, MED_BLUE),
("INNERGRID", (0,0), (-1,-1), 0.5, MID_GREY),
("TOPPADDING", (0,0), (-1,-1), 7),
("BOTTOMPADDING", (0,0), (-1,-1), 7),
("LEFTPADDING", (0,0), (-1,-1), 8),
("VALIGN", (0,0), (-1,-1), "MIDDLE"),
]))
story.append(sched_table)
story.append(Spacer(1, 10))
# ══════════════════════════════════════════════════════════════════════════
# SECTION 6 - DO's and DON'Ts
# ══════════════════════════════════════════════════════════════════════════
story.append(section_header("DO'S AND DON'TS"))
story.append(Spacer(1, 8))
dos_donts = [
[Paragraph("<b>DO</b>", style("DH", fontName="Helvetica-Bold", fontSize=10,
textColor=WHITE, alignment=TA_CENTER)),
Paragraph("<b>DON'T</b>", style("DH2", fontName="Helvetica-Bold", fontSize=10,
textColor=WHITE, alignment=TA_CENTER))],
[Paragraph(
"✔ Warm up before every session<br/>"
"✔ Exercise 2-3 times daily consistently<br/>"
"✔ Use the arm for light daily tasks<br/>"
"✔ Sleep with a pillow supporting the arm<br/>"
"✔ Take pain relief before exercise if needed<br/>"
"✔ Keep track of your range each week<br/>"
"✔ See your physiotherapist regularly",
style("DoText", fontName="Helvetica", fontSize=9.5,
textColor=HexColor("#1B5E20"), leading=16)),
Paragraph(
"✘ Do not immobilise or rest the shoulder completely<br/>"
"✘ Do not push through sharp, severe pain<br/>"
"✘ Do not sleep directly on the affected shoulder<br/>"
"✘ Do not carry heavy bags on that side<br/>"
"✘ Do not reach suddenly or jerk the arm<br/>"
"✘ Do not skip sessions - consistency is key<br/>"
"✘ Do not give up - recovery takes months",
style("DontText", fontName="Helvetica", fontSize=9.5,
textColor=HexColor("#B71C1C"), leading=16))],
]
dd_table = Table(dos_donts, colWidths=[CONTENT_W/2, CONTENT_W/2])
dd_table.setStyle(TableStyle([
("BACKGROUND", (0,0), (0,0), HexColor("#2E7D32")),
("BACKGROUND", (1,0), (1,0), HexColor("#C62828")),
("BACKGROUND", (0,1), (0,1), HexColor("#E8F5E9")),
("BACKGROUND", (1,1), (1,1), HexColor("#FFEBEE")),
("BOX", (0,0), (-1,-1), 1, MID_GREY),
("INNERGRID", (0,0), (-1,-1), 0.5, MID_GREY),
("TOPPADDING", (0,0), (-1,-1), 8),
("BOTTOMPADDING", (0,0), (-1,-1), 8),
("LEFTPADDING", (0,0), (-1,-1), 10),
("RIGHTPADDING", (0,0), (-1,-1), 10),
("VALIGN", (0,0), (-1,-1), "TOP"),
]))
story.append(dd_table)
story.append(Spacer(1, 10))
# ══════════════════════════════════════════════════════════════════════════
# SECTION 7 - PROGRESS TRACKER
# ══════════════════════════════════════════════════════════════════════════
story.append(section_header("WEEKLY PROGRESS TRACKER (Print and fill in)", color=HexColor("#4A148C")))
story.append(Spacer(1, 8))
tracker_header = [
Paragraph("<b>Week</b>", S_TABLE_HDR),
Paragraph("<b>Date</b>", S_TABLE_HDR),
Paragraph("<b>Pain (0-10)</b>", S_TABLE_HDR),
Paragraph("<b>Arm raised to</b>", S_TABLE_HDR),
Paragraph("<b>Exercises done</b>", S_TABLE_HDR),
Paragraph("<b>Notes</b>", S_TABLE_HDR),
]
tracker_rows = [tracker_header]
for i in range(1, 9):
tracker_rows.append([
Paragraph(f"Week {i}", S_TABLE_CELL), "", "", "", "", ""
])
tracker_table = Table(tracker_rows,
colWidths=[1.4*cm, 2.2*cm, 2.5*cm, 2.8*cm, 3.0*cm, CONTENT_W-11.9*cm])
tracker_table.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,0), HexColor("#4A148C")),
("ROWBACKGROUNDS",(0,1), (-1,-1), [WHITE, HexColor("#F3E5F5")]),
("BOX", (0,0), (-1,-1), 1, MID_GREY),
("INNERGRID", (0,0), (-1,-1), 0.5, MID_GREY),
("TOPPADDING", (0,0), (-1,-1), 6),
("BOTTOMPADDING", (0,0), (-1,-1), 6),
("LEFTPADDING", (0,0), (-1,-1), 6),
("VALIGN", (0,0), (-1,-1), "MIDDLE"),
("FONTSIZE", (0,1), (-1,-1), 9),
]))
story.append(tracker_table)
story.append(Spacer(1, 10))
# ══════════════════════════════════════════════════════════════════════════
# SECTION 8 - WHEN TO SEEK HELP
# ══════════════════════════════════════════════════════════════════════════
story.append(section_header("WHEN TO SEEK MEDICAL ATTENTION", color=HexColor("#B71C1C")))
story.append(Spacer(1, 8))
warning_items = [
"Sudden severe worsening of pain",
"Redness, swelling, or warmth around the shoulder joint (may indicate infection)",
"Fever with shoulder pain",
"Pain radiating down the arm with tingling or numbness",
"No improvement after 3-4 months of consistent exercises",
"A fall or trauma causing sudden increased pain (rule out fracture)",
]
for item in warning_items:
story.append(Paragraph(f"<font color='#C62828'><b>!</b></font> {item}", S_BULLET))
story.append(Spacer(1, 3))
story.append(Spacer(1, 8))
# ══════════════════════════════════════════════════════════════════════════
# FOOTER
# ══════════════════════════════════════════════════════════════════════════
story.append(HRFlowable(width=CONTENT_W, thickness=1, color=MED_BLUE))
story.append(Spacer(1, 6))
story.append(Paragraph(
"This guide is for general information only and does not replace professional medical advice. "
"Always consult your doctor or physiotherapist before starting or changing an exercise programme. "
"Prepared by Orris Medical Assistant - July 2026.",
S_NOTE
))
# ══════════════════════════════════════════════════════════════════════════
# BUILD
# ══════════════════════════════════════════════════════════════════════════
doc.build(story)
print(f"PDF created: {OUTPUT}")
python3 /tmp/workspace/frozen-shoulder/generate_pdf.py 2>&1
ls -lh /tmp/workspace/frozen-shoulder/Frozen_Shoulder_Exercise_Guide.pdf
Frozen Shoulder Exercise Guide
PDF Document · PDF