Hearing aid care during monsoon for both battery operated hearing aids and also rechargeable hearing aids
hearing aid care monsoon humidity battery rechargeable tips 2025
rechargeable hearing aid moisture protection rainy season care guide
| Practice | What to Do |
|---|---|
| Daily wipe-down | Use a soft, dry lint-free cloth every evening to wipe the shell, microphone ports, and receiver |
| Brush the ports | Use the cleaning brush (provided with your aid) to clear microphone and sound port openings of moisture droplets or debris |
| Avoid bathroom storage | Never store aids in the bathroom - it is the highest-humidity room in the house |
| Protective cover | Use a moisture-resistant sleeve or "hearing aid sweatband" over the device when outdoors |
| Hat or umbrella rule | Always wear a hat or carry an umbrella - direct rain on hearing aids should be avoided even for IP68-rated devices |
| Remove before exposure | Remove aids before stepping into heavy rain, entering a steam room, or doing intense exercise |
| Professional check | Schedule a professional clean and check at the start of and midway through monsoon season |
| Tool | How It Works | Best For |
|---|---|---|
| Silica gel desiccant jar | Passive absorption of moisture | Both types - budget option, replace silica monthly |
| Electronic drying box | Gentle heat + airflow | Both types - most effective daily dryer |
| UV-C drying box | UV light kills bacteria + desiccant | Both types - also sanitizes |
| Charging case with dryer | Built-in dryer during charging | Rechargeable only |
Create a reference guide PDF on hearing aid monsoon care to share with family
ls -la /tmp/workspace/ 2>/dev/null || echo "workspace does not exist"
mkdir -p /tmp/workspace/hearing-aid-guide
~/hearing-aid-guide/create_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_JUSTIFY
from reportlab.platypus import (
SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
HRFlowable, KeepTogether, PageBreak
)
from reportlab.platypus.flowables import Flowable
from reportlab.lib.colors import HexColor
# ── Colour palette ────────────────────────────────────────────────────────────
TEAL = HexColor("#0B6E8C") # dark teal – headings
TEAL_LIGHT = HexColor("#E6F4F8") # very light teal – section fills
SKY = HexColor("#1BAACC") # medium teal – accents
ORANGE = HexColor("#E87722") # orange – battery icon accent
GREEN = HexColor("#2E8B57") # green – rechargeable accent
RED_SOFT = HexColor("#C0392B") # red – emergency tips
GREY = HexColor("#555555")
LIGHT_GREY = HexColor("#F5F5F5")
WHITE = colors.white
# ── Custom Flowable: Coloured Rounded Box Header ──────────────────────────────
class SectionHeader(Flowable):
"""A rounded-rect header band for major sections."""
def __init__(self, text, bg=TEAL, fg=WHITE, width=None, height=28):
super().__init__()
self.text = text
self.bg = bg
self.fg = fg
self._width = width
self.height = height
def wrap(self, aW, aH):
self.box_width = self._width or aW
return self.box_width, self.height + 4
def draw(self):
c = self.canv
w, h = self.box_width, self.height
c.setFillColor(self.bg)
c.roundRect(0, 0, w, h, 6, fill=1, stroke=0)
c.setFillColor(self.fg)
c.setFont("Helvetica-Bold", 11)
c.drawString(10, h / 2 - 5, self.text)
class IconBullet(Flowable):
"""A small circle bullet with a label beside it."""
def __init__(self, icon_char, label, icon_color=TEAL, font_size=9):
super().__init__()
self.icon = icon_char
self.label = label
self.color = icon_color
self.fs = font_size
def wrap(self, aW, aH):
self._w = aW
return aW, self.fs + 6
def draw(self):
c = self.canv
r = self.fs / 2 + 1
c.setFillColor(self.color)
c.circle(r, r - 1, r, fill=1, stroke=0)
c.setFillColor(WHITE)
c.setFont("Helvetica-Bold", self.fs - 2)
c.drawCentredString(r, r - 3, self.icon)
c.setFillColor(GREY)
c.setFont("Helvetica", self.fs)
c.drawString(r * 2 + 6, r - 4, self.label)
# ── Helper: build a two-column tip table ─────────────────────────────────────
def tip_table(rows, col_widths=None, row_bg=LIGHT_GREY):
"""rows = list of (label, value) tuples"""
page_w = A4[0] - 3.6 * cm # usable width
if col_widths is None:
col_widths = [page_w * 0.38, page_w * 0.62]
data = [["Action", "What to Do"]] + list(rows)
style = TableStyle([
# Header row
("BACKGROUND", (0, 0), (-1, 0), TEAL),
("TEXTCOLOR", (0, 0), (-1, 0), WHITE),
("FONTNAME", (0, 0), (-1, 0), "Helvetica-Bold"),
("FONTSIZE", (0, 0), (-1, 0), 9),
("BOTTOMPADDING",(0, 0), (-1, 0), 6),
("TOPPADDING", (0, 0), (-1, 0), 6),
# Alternating rows
*[("BACKGROUND", (0, i), (-1, i), LIGHT_GREY if i % 2 == 0 else WHITE)
for i in range(1, len(data))],
("FONTNAME", (0, 1), (-1, -1), "Helvetica"),
("FONTSIZE", (0, 1), (-1, -1), 8.5),
("TOPPADDING", (0, 1), (-1, -1), 4),
("BOTTOMPADDING",(0, 1), (-1, -1), 4),
("LEFTPADDING", (0, 0), (-1, -1), 6),
("GRID", (0, 0), (-1, -1), 0.3, HexColor("#CCCCCC")),
("VALIGN", (0, 0), (-1, -1), "MIDDLE"),
("ROWBACKGROUNDS", (0, 1), (-1, -1), [WHITE, TEAL_LIGHT]),
])
return Table(data, colWidths=col_widths, style=style, repeatRows=1)
def checklist_table(items, accent=TEAL):
"""Single-column checklist with tick boxes."""
page_w = A4[0] - 3.6 * cm
data = [[" ☐ " + item] for item in items]
style = TableStyle([
("FONTNAME", (0, 0), (-1, -1), "Helvetica"),
("FONTSIZE", (0, 0), (-1, -1), 9),
("TOPPADDING", (0, 0), (-1, -1), 4),
("BOTTOMPADDING",(0, 0), (-1, -1), 4),
("LEFTPADDING", (0, 0), (-1, -1), 8),
("ROWBACKGROUNDS", (0, 0), (-1, -1), [WHITE, TEAL_LIGHT]),
("GRID", (0, 0), (-1, -1), 0.3, HexColor("#CCCCCC")),
])
return Table(data, colWidths=[page_w], style=style)
def emergency_table(rows):
page_w = A4[0] - 3.6 * cm
data = [["Step", "Action"]] + list(rows)
style = TableStyle([
("BACKGROUND", (0, 0), (-1, 0), RED_SOFT),
("TEXTCOLOR", (0, 0), (-1, 0), WHITE),
("FONTNAME", (0, 0), (-1, 0), "Helvetica-Bold"),
("FONTSIZE", (0, 0), (-1, 0), 9),
("BOTTOMPADDING",(0, 0), (-1, 0), 6),
("TOPPADDING", (0, 0), (-1, 0), 6),
("FONTNAME", (0, 1), (-1, -1), "Helvetica"),
("FONTSIZE", (0, 1), (-1, -1), 8.5),
("TOPPADDING", (0, 1), (-1, -1), 4),
("BOTTOMPADDING",(0, 1), (-1, -1), 4),
("LEFTPADDING", (0, 0), (-1, -1), 6),
("GRID", (0, 0), (-1, -1), 0.3, HexColor("#CCCCCC")),
("VALIGN", (0, 0), (-1, -1), "MIDDLE"),
("ROWBACKGROUNDS", (0, 1), (-1, -1), [WHITE, HexColor("#FDECEA")]),
("FONTNAME", (0, 1), (-1, -1), "Helvetica"),
("TEXTCOLOR", (0, 1), (0, -1), RED_SOFT),
("FONTNAME", (0, 1), (0, -1), "Helvetica-Bold"),
])
return Table(data, colWidths=[page_w * 0.12, page_w * 0.88], style=style)
# ── Page template callbacks ───────────────────────────────────────────────────
def on_first_page(canvas, doc):
canvas.saveState()
# Top banner
canvas.setFillColor(TEAL)
canvas.rect(0, A4[1] - 2.4 * cm, A4[0], 2.4 * cm, fill=1, stroke=0)
# Bottom strip
canvas.setFillColor(SKY)
canvas.rect(0, 0, A4[0], 0.7 * cm, fill=1, stroke=0)
canvas.setFillColor(WHITE)
canvas.setFont("Helvetica", 7.5)
canvas.drawCentredString(A4[0] / 2, 0.22 * cm,
"Reference guide prepared July 2026 · Consult your audiologist for device-specific advice")
canvas.restoreState()
def on_later_pages(canvas, doc):
canvas.saveState()
# Thin top accent
canvas.setFillColor(TEAL)
canvas.rect(0, A4[1] - 0.6 * cm, A4[0], 0.6 * cm, fill=1, stroke=0)
canvas.setFillColor(WHITE)
canvas.setFont("Helvetica-Bold", 7.5)
canvas.drawString(1.8 * cm, A4[1] - 0.42 * cm,
"Hearing Aid Monsoon Care Guide")
canvas.setFont("Helvetica", 7.5)
canvas.drawRightString(A4[0] - 1.8 * cm, A4[1] - 0.42 * cm,
f"Page {doc.page}")
# Bottom strip
canvas.setFillColor(SKY)
canvas.rect(0, 0, A4[0], 0.7 * cm, fill=1, stroke=0)
canvas.setFillColor(WHITE)
canvas.setFont("Helvetica", 7.5)
canvas.drawCentredString(A4[0] / 2, 0.22 * cm,
"Reference guide prepared July 2026 · Consult your audiologist for device-specific advice")
canvas.restoreState()
# ── Build document ────────────────────────────────────────────────────────────
out_path = "/tmp/workspace/hearing-aid-guide/Hearing_Aid_Monsoon_Care_Guide.pdf"
doc = SimpleDocTemplate(
out_path,
pagesize=A4,
leftMargin=1.8 * cm,
rightMargin=1.8 * cm,
topMargin=3.2 * cm,
bottomMargin=1.6 * cm,
title="Hearing Aid Monsoon Care Guide",
author="Orris Health Guide",
)
styles = getSampleStyleSheet()
# Custom styles
title_style = ParagraphStyle("GuideTitle",
fontName="Helvetica-Bold", fontSize=22, textColor=WHITE,
leading=26, alignment=TA_CENTER, spaceAfter=4)
subtitle_style = ParagraphStyle("GuideSubtitle",
fontName="Helvetica", fontSize=11, textColor=HexColor("#CCE9F5"),
leading=14, alignment=TA_CENTER, spaceAfter=2)
date_style = ParagraphStyle("GuideDate",
fontName="Helvetica-Oblique", fontSize=9, textColor=HexColor("#B0D8EC"),
alignment=TA_CENTER)
body = ParagraphStyle("Body",
fontName="Helvetica", fontSize=9, textColor=GREY,
leading=13, spaceAfter=4, spaceBefore=2, alignment=TA_JUSTIFY)
bold_body = ParagraphStyle("BoldBody",
fontName="Helvetica-Bold", fontSize=9, textColor=GREY,
leading=13, spaceAfter=2)
sub_head = ParagraphStyle("SubHead",
fontName="Helvetica-Bold", fontSize=10, textColor=TEAL,
leading=14, spaceBefore=8, spaceAfter=3)
warn_box = ParagraphStyle("WarnBox",
fontName="Helvetica-Bold", fontSize=9, textColor=RED_SOFT,
backColor=HexColor("#FDECEA"), leading=13,
leftIndent=8, rightIndent=8, spaceBefore=4, spaceAfter=4,
borderPad=6)
tip_box = ParagraphStyle("TipBox",
fontName="Helvetica", fontSize=9, textColor=HexColor("#1B5E20"),
backColor=HexColor("#E8F5E9"), leading=13,
leftIndent=8, rightIndent=8, spaceBefore=4, spaceAfter=4)
story = []
# ── COVER (first page header handled by canvas callback) ─────────────────────
story.append(Spacer(1, 0.3 * cm))
story.append(Paragraph("Hearing Aid", title_style))
story.append(Paragraph("Monsoon Care Guide", title_style))
story.append(Spacer(1, 0.1 * cm))
story.append(Paragraph("Battery-Operated & Rechargeable Hearing Aids", subtitle_style))
story.append(Paragraph("July 2026 · Share freely with family & caregivers", date_style))
story.append(Spacer(1, 0.5 * cm))
# Intro blurb
intro_data = [[
Paragraph(
"<b>Why monsoon is the riskiest season for hearing aids:</b> Humidity levels of "
"80–100%, continuous rain, and sudden temperature changes cause condensation inside "
"devices, corroding circuits, blocking microphone ports, draining batteries, and "
"distorting sound. A few minutes of daily care can prevent costly repairs and keep "
"your aids working perfectly all season.",
body)
]]
intro_table = Table(intro_data,
colWidths=[A4[0] - 3.6 * cm],
style=TableStyle([
("BACKGROUND", (0, 0), (-1, -1), TEAL_LIGHT),
("BOX", (0, 0), (-1, -1), 1, SKY),
("LEFTPADDING", (0, 0), (-1, -1), 10),
("RIGHTPADDING",(0, 0), (-1, -1), 10),
("TOPPADDING", (0, 0), (-1, -1), 8),
("BOTTOMPADDING",(0,0), (-1, -1), 8),
]))
story.append(intro_table)
story.append(Spacer(1, 0.4 * cm))
# ── SECTION 1 – Why Moisture Is Dangerous ────────────────────────────────────
story.append(SectionHeader("1. Why Moisture Is Dangerous for Hearing Aids"))
story.append(Spacer(1, 0.2 * cm))
risks = [
("Corrosion", "Moisture corrodes internal circuit boards and battery contacts, causing permanent failure"),
("Blocked ports", "Water droplets block microphone and receiver openings, muffling or cutting out sound"),
("Battery drain", "Humidity shortens disposable battery life by 30–50% and degrades rechargeable batteries"),
("Fungal growth", "Persistent moisture inside tubing and ear moulds encourages mould and bacteria"),
("Condensation", "Moving between AC rooms and humid outdoors creates condensation inside the shell"),
]
story.append(tip_table(risks))
story.append(Spacer(1, 0.35 * cm))
# ── SECTION 2 – General Rules (Both Types) ───────────────────────────────────
story.append(SectionHeader("2. General Rules — Both Battery & Rechargeable Aids"))
story.append(Spacer(1, 0.2 * cm))
general = [
("Daily wipe-down", "Each evening wipe the shell, microphone ports and receiver with a soft dry lint-free cloth"),
("Brush the ports", "Use your cleaning brush daily on microphone openings to clear moisture droplets and debris"),
("Never bathroom storage", "Bathrooms are high-humidity zones — never store aids on the sink shelf or medicine cabinet"),
("Protective sleeve", "Use a moisture-resistant hearing aid sleeve or 'sweatband' when outdoors in humid weather"),
("Hat or umbrella rule", "Always cover your ears in rain — even IP-rated devices should not take direct rain"),
("Remove before exposure", "Remove aids before stepping into heavy rain, steam rooms or intense exercise"),
("Skip hairspray first", "Apply hairspray, sunscreen or insect repellent BEFORE inserting aids, then wash hands"),
("Professional check", "Schedule a professional clean at the start of monsoon and once mid-season"),
]
story.append(tip_table(general))
story.append(Spacer(1, 0.35 * cm))
# ── SECTION 3 – Battery-Operated Aids ────────────────────────────────────────
story.append(SectionHeader("3. Battery-Operated Hearing Aids — Monsoon Care",
bg=ORANGE, fg=WHITE))
story.append(Spacer(1, 0.2 * cm))
story.append(Paragraph("Nightly Routine", sub_head))
nightly_bat = [
("1. Open battery door", "Open it COMPLETELY every night — allows air circulation and prevents trapped moisture corroding contacts"),
("2. Use a drying box", "Place aids (door open) in an electronic UV drying box or desiccant jar for 6–8 hours overnight"),
("3. Store spare batteries", "Keep spares in a sealed airtight container with a silica gel packet; never in a damp pocket"),
]
story.append(tip_table(nightly_bat,
col_widths=[(A4[0]-3.6*cm)*0.32, (A4[0]-3.6*cm)*0.68]))
story.append(Spacer(1, 0.25 * cm))
story.append(Paragraph("Battery-Specific Monsoon Tips", sub_head))
bat_tips = [
("Change batteries more often",
"Humidity shortens battery life — expect 5–6 days instead of the usual 7–10; carry a spare pair"),
("Check for corrosion",
"Look for white or greenish residue on battery contacts; clean gently with a dry cotton swab"),
("Never insert damp battery",
"Dry each battery completely before inserting — moisture bridges the contacts and drains power instantly"),
("Check battery door seal",
"A loose-fitting battery door allows moisture ingress; have it checked if it does not close firmly"),
("Remove for long storage",
"If aids will not be used for more than a day, remove batteries entirely to prevent corrosion"),
]
story.append(tip_table(bat_tips,
col_widths=[(A4[0]-3.6*cm)*0.32, (A4[0]-3.6*cm)*0.68]))
story.append(Spacer(1, 0.35 * cm))
# ── SECTION 4 – Rechargeable Aids ────────────────────────────────────────────
story.append(SectionHeader("4. Rechargeable Hearing Aids — Monsoon Care",
bg=GREEN, fg=WHITE))
story.append(Spacer(1, 0.2 * cm))
story.append(Paragraph(
"<b>Key difference:</b> You <b>cannot open the battery compartment</b> on rechargeable aids, "
"so the entire burden of moisture protection falls on your external drying routine. "
"This makes nightly drying even more critical.",
body))
story.append(Spacer(1, 0.15 * cm))
story.append(Paragraph("Nightly Routine", sub_head))
nightly_rec = [
("1. Power off first",
"Press and hold the power button to turn the aids off before placing them in the charger"),
("2. Wipe charging contacts",
"Wipe the metal charging contacts on the bottom of each aid with a dry cloth before charging — damp contacts cause faults"),
("3. Use charging case",
"Place in the charging case overnight; many modern cases (Phonak, ReSound, Starkey) have a built-in dehumidifier"),
("4. Separate dryer if needed",
"If your case has no built-in dryer, use an electronic drying box for 30–60 min before placing in the charger"),
]
story.append(tip_table(nightly_rec,
col_widths=[(A4[0]-3.6*cm)*0.32, (A4[0]-3.6*cm)*0.68]))
story.append(Spacer(1, 0.25 * cm))
story.append(Paragraph("Rechargeable-Specific Monsoon Tips", sub_head))
rec_tips = [
("Never charge while wet",
"Always wipe aids completely dry BEFORE placing in the charger — moisture on contacts causes short circuits"),
("Monitor charge capacity",
"If the battery no longer holds a full charge after heavy monsoon exposure, moisture may have degraded it — see your audiologist"),
("No hairdryer or oven",
"Never use heat to speed drying — it damages the lithium-ion battery and voids the warranty"),
("Check IP rating",
"IP67/IP68-rated aids (e.g. Starkey Genesis AI, Phonak Infinio) handle rain and splashes far better; ask your audiologist"),
("Firmware updates",
"Moisture can cause firmware glitches; check for updates if the aid behaves erratically after heavy humidity exposure"),
]
story.append(tip_table(rec_tips,
col_widths=[(A4[0]-3.6*cm)*0.32, (A4[0]-3.6*cm)*0.68]))
story.append(Spacer(1, 0.35 * cm))
# ── SECTION 5 – Drying Tools ─────────────────────────────────────────────────
story.append(SectionHeader("5. Drying Tools — Which to Use"))
story.append(Spacer(1, 0.2 * cm))
dryer_rows = [
("Silica gel desiccant jar",
"Passive absorption; budget option; replace silica monthly; good for travel backup",
"Both"),
("Electronic drying box",
"Gentle heat + airflow; most effective daily dryer; 6–8 hours overnight is gold standard",
"Both"),
("UV-C drying box",
"UV light kills bacteria and fungi + desiccant; best for monsoon due to infection risk",
"Both"),
("Charging case with dryer",
"Dries while charging; most convenient for rechargeable users; check your model specs",
"Rechargeable only"),
]
dryer_data = [["Tool", "How It Works", "For"]] + dryer_rows
dryer_widths = [(A4[0]-3.6*cm)*0.26, (A4[0]-3.6*cm)*0.55, (A4[0]-3.6*cm)*0.19]
dryer_style = TableStyle([
("BACKGROUND", (0, 0), (-1, 0), TEAL),
("TEXTCOLOR", (0, 0), (-1, 0), WHITE),
("FONTNAME", (0, 0), (-1, 0), "Helvetica-Bold"),
("FONTSIZE", (0, 0), (-1, 0), 9),
("BOTTOMPADDING",(0, 0), (-1, 0), 6),
("TOPPADDING", (0, 0), (-1, 0), 6),
("FONTNAME", (0, 1), (-1, -1), "Helvetica"),
("FONTSIZE", (0, 1), (-1, -1), 8.5),
("TOPPADDING", (0, 1), (-1, -1), 4),
("BOTTOMPADDING",(0, 1), (-1, -1), 4),
("LEFTPADDING", (0, 0), (-1, -1), 6),
("GRID", (0, 0), (-1, -1), 0.3, HexColor("#CCCCCC")),
("VALIGN", (0, 0), (-1, -1), "MIDDLE"),
("ROWBACKGROUNDS", (0, 1), (-1, -1), [WHITE, TEAL_LIGHT]),
("TEXTCOLOR", (2, 4), (2, 4), GREEN),
("FONTNAME", (2, 4), (2, 4), "Helvetica-Bold"),
])
story.append(Table(dryer_data, colWidths=dryer_widths, style=dryer_style, repeatRows=1))
story.append(Spacer(1, 0.35 * cm))
# ── SECTION 6 – Emergency: Aid Gets Wet ──────────────────────────────────────
story.append(SectionHeader("6. Emergency: Hearing Aid Got Wet — Act Immediately",
bg=RED_SOFT, fg=WHITE))
story.append(Spacer(1, 0.2 * cm))
emerg_rows = [
("1", "Remove the aid from water/rain IMMEDIATELY"),
("2", "Power OFF — for rechargeable aids press and hold the power button"),
("3", "Do NOT press any other buttons — pushing buttons drives moisture deeper inside"),
("4", "Wipe exterior gently with a dry lint-free cloth, including all ports and contacts"),
("5", "Battery aids: remove the battery and leave the door fully open"),
("6", "Place in an electronic drying box or desiccant jar for 24–48 hours"),
("7", "Rechargeable aids: do NOT put back in the charger until completely dry"),
("8", "Do NOT use a rice bag — rice dust clogs microphone ports"),
("9", "Do NOT use a hairdryer, oven, or microwave to speed drying"),
("10","If the aid still fails after 48 hours, contact your audiologist immediately for inspection"),
]
story.append(emergency_table(emerg_rows))
story.append(Spacer(1, 0.35 * cm))
# ── SECTION 7 – Checklist ────────────────────────────────────────────────────
story.append(SectionHeader("7. Daily Monsoon Checklist — Print & Tick Off"))
story.append(Spacer(1, 0.2 * cm))
checklist_items = [
"Wiped hearing aids with dry cloth before storing",
"Brushed microphone and sound ports with cleaning brush",
"Battery door opened fully for the night (battery aids)",
"Charging contacts wiped dry before placing in charger (rechargeable aids)",
"Placed aids in drying box / desiccant jar / charging case",
"Checked battery level and replaced if weak (battery aids)",
"Spare batteries stored in airtight container with silica gel (battery aids)",
"Wore protective sleeve or cover when outdoors today",
"Avoided hairspray / sunscreen while aids were in",
"Did NOT store aids in the bathroom",
"Professional check booked for mid-monsoon",
]
story.append(checklist_table(checklist_items))
story.append(Spacer(1, 0.35 * cm))
# ── SECTION 8 – Quick Comparison ─────────────────────────────────────────────
story.append(SectionHeader("8. Quick Comparison — Battery vs Rechargeable"))
story.append(Spacer(1, 0.2 * cm))
compare_data = [
["Feature", "Battery-Operated", "Rechargeable"],
["Open battery door nightly?", "YES — mandatory", "No — sealed unit"],
["Nightly drying method", "Desiccant jar / electronic dryer (door open)", "Charging case (with dryer) or separate dryer first"],
["Battery life in monsoon", "Shorter — change every 5–6 days", "Monitor charge capacity; degradation possible"],
["Corrosion risk", "Battery contacts + internal circuits", "Charging contacts + internal circuits"],
["If it gets wet", "Remove battery, open door, dry 24–48 hrs", "Power off, wipe contacts, dry 24–48 hrs before charging"],
["IP water resistance", "Varies by model — check specs", "Often higher (IP67/68) on modern models"],
["Travel drying", "Desiccant jar + spare batteries", "Portable charging case; carry backup charger"],
]
compare_widths = [(A4[0]-3.6*cm)*0.28, (A4[0]-3.6*cm)*0.36, (A4[0]-3.6*cm)*0.36]
compare_style = TableStyle([
("BACKGROUND", (0, 0), (-1, 0), TEAL),
("TEXTCOLOR", (0, 0), (-1, 0), WHITE),
("FONTNAME", (0, 0), (-1, 0), "Helvetica-Bold"),
("FONTSIZE", (0, 0), (-1, 0), 9),
("BACKGROUND", (0, 1), (0, -1), TEAL_LIGHT),
("FONTNAME", (0, 1), (0, -1), "Helvetica-Bold"),
("FONTSIZE", (0, 1), (-1, -1), 8.5),
("TOPPADDING", (0, 0), (-1, -1), 5),
("BOTTOMPADDING",(0, 0), (-1, -1), 5),
("LEFTPADDING", (0, 0), (-1, -1), 6),
("GRID", (0, 0), (-1, -1), 0.3, HexColor("#CCCCCC")),
("VALIGN", (0, 0), (-1, -1), "MIDDLE"),
("ROWBACKGROUNDS", (0, 1), (-1, -1), [WHITE, HexColor("#F0F8FF")]),
("TEXTCOLOR", (1, 1), (1, -1), HexColor("#7B3F00")),
("TEXTCOLOR", (2, 1), (2, -1), HexColor("#1B5E20")),
])
story.append(Table(compare_data, colWidths=compare_widths,
style=compare_style, repeatRows=1))
story.append(Spacer(1, 0.35 * cm))
# ── SECTION 9 – Warning Signs ─────────────────────────────────────────────────
story.append(SectionHeader("9. Warning Signs — See Your Audiologist",
bg=ORANGE, fg=WHITE))
story.append(Spacer(1, 0.2 * cm))
warning_items = [
"Sound is muffled, distorted, or cuts in and out after rain exposure",
"Battery drains much faster than usual (within 1–2 days for disposable batteries)",
"Rechargeable aid does not hold a full charge after monsoon exposure",
"Visible corrosion (white or green residue) on battery contacts",
"Aid stopped working after getting wet, even after 48-hour drying",
"Whistling or feedback that was not present before",
"Charging contacts feel sticky or show discolouration",
"Device does not switch on at all",
]
story.append(checklist_table(warning_items, accent=ORANGE))
story.append(Spacer(1, 0.3 * cm))
story.append(Paragraph(
"⚠ <b>Do not attempt to open or repair the hearing aid yourself.</b> "
"Internal moisture damage requires professional equipment (ultrasonic cleaning, vacuum drying) "
"that only a certified audiologist or service centre can provide.",
warn_box))
story.append(Spacer(1, 0.4 * cm))
# ── CLOSING NOTE ─────────────────────────────────────────────────────────────
closing_data = [[Paragraph(
"<b>Remember:</b> Hearing aids are precision electronic devices. The monsoon daily care routine "
"takes under five minutes and will significantly extend the life of your device, saving on "
"costly repairs. When in doubt, always consult your audiologist. Share this guide with "
"every family member who helps care for a hearing aid user.",
body)]]
closing_table = Table(closing_data,
colWidths=[A4[0] - 3.6 * cm],
style=TableStyle([
("BACKGROUND", (0, 0), (-1, -1), TEAL),
("TEXTCOLOR", (0, 0), (-1, -1), WHITE),
("LEFTPADDING", (0, 0), (-1, -1), 10),
("RIGHTPADDING",(0, 0), (-1, -1), 10),
("TOPPADDING", (0, 0), (-1, -1), 10),
("BOTTOMPADDING",(0,0), (-1, -1), 10),
("FONTNAME", (0, 0), (-1, -1), "Helvetica"),
]))
# Override text color for white-on-teal paragraph
closing_data[0][0] = Paragraph(
"<b>Remember:</b> Hearing aids are precision electronic devices. The monsoon daily care routine "
"takes under five minutes and will significantly extend the life of your device, saving on "
"costly repairs. When in doubt, always consult your audiologist. Share this guide with "
"every family member who helps care for a hearing aid user.",
ParagraphStyle("ClosingP",
fontName="Helvetica", fontSize=9, textColor=WHITE,
leading=13, alignment=TA_JUSTIFY))
story.append(closing_table)
# ── Build ─────────────────────────────────────────────────────────────────────
doc.build(story,
onFirstPage=on_first_page,
onLaterPages=on_later_pages)
print(f"PDF created: {out_path}")
cd /tmp/workspace/hearing-aid-guide && python create_pdf.py 2>&1
ls -lh /tmp/workspace/hearing-aid-guide/Hearing_Aid_Monsoon_Care_Guide.pdf
Hearing Aid Monsoon Care Guide
PDF Document · PDF
| Section | Content |
|---|---|
| 1. Why Moisture Is Dangerous | 5 key risks: corrosion, blocked ports, battery drain, fungal growth, condensation |
| 2. General Rules | 8 rules that apply to both aid types |
| 3. Battery-Operated Aids | Nightly routine + 5 monsoon-specific tips (corrosion checks, battery frequency, door seals) |
| 4. Rechargeable Aids | Nightly routine + 5 tips (charging contacts, IP ratings, no-heat rule) |
| 5. Drying Tools | Comparison of 4 dryer types with "who it's best for" |
| 6. Emergency: Aid Got Wet | 10-step emergency action plan in a red-highlighted table |
| 7. Daily Checklist | 11-item printable tick-off checklist for daily use |
| 8. Battery vs Rechargeable Comparison | Side-by-side table across 7 parameters |
| 9. Warning Signs | 8 signs to call your audiologist immediately |
Generate a reel formate for this
~/hearing-aid-guide/create_reel.py
"""
Instagram/Reel Carousel – Hearing Aid Monsoon Care
9 slides · 1080 × 1920 px · Portrait
Uses reportlab canvas for full pixel control.
"""
from reportlab.pdfgen import canvas
from reportlab.lib.colors import HexColor, white, black
from reportlab.lib.units import inch
from reportlab.lib.utils import ImageReader
from io import BytesIO
import math
# ── Canvas size (1080 × 1920 px at 96 dpi → points) ─────────────────────────
PX_W, PX_H = 1080, 1920
PT_W = PX_W * 72 / 96 # 810 pt
PT_H = PX_H * 72 / 96 # 1440 pt
# ── Colour palette ────────────────────────────────────────────────────────────
TEAL = HexColor("#0B6E8C")
TEAL2 = HexColor("#0E8FAF")
TEAL_DARK = HexColor("#094F66")
TEAL_LIGHT = HexColor("#D0EEF7")
ORANGE = HexColor("#E87722")
ORANGE_L = HexColor("#FDE8D3")
GREEN = HexColor("#2E8B57")
GREEN_L = HexColor("#D4EDDA")
RED = HexColor("#C0392B")
RED_L = HexColor("#FDECEA")
YELLOW = HexColor("#F5C518")
CREAM = HexColor("#FFF8F0")
WHITE = white
GREY_DARK = HexColor("#333333")
GREY_MID = HexColor("#666666")
GREY_LIGHT = HexColor("#F0F0F0")
out_path = "/tmp/workspace/hearing-aid-guide/Hearing_Aid_Monsoon_Reel.pdf"
c = canvas.Canvas(out_path, pagesize=(PT_W, PT_H))
# ── Utility helpers ───────────────────────────────────────────────────────────
def cx(frac=0.5): return PT_W * frac
def cy(frac=0.5): return PT_H * frac
def bg_gradient_rect(c, x, y, w, h, top_col, bot_col, steps=40):
"""Simulate vertical gradient with thin horizontal strips."""
tr, tg, tb = top_col.red, top_col.green, top_col.blue
br, bg_, bb = bot_col.red, bot_col.green, bot_col.blue
strip_h = h / steps
for i in range(steps):
t = i / steps
r = tr + (br - tr) * t
g = tg + (bg_ - tg) * t
b = tb + (bb - tb) * t
c.setFillColorRGB(r, g, b)
c.rect(x, y + h - (i + 1) * strip_h, w, strip_h + 0.5, fill=1, stroke=0)
def draw_full_bg(c, top_col, bot_col):
bg_gradient_rect(c, 0, 0, PT_W, PT_H, top_col, bot_col)
def pill(c, x, y, w, h, fill_col, radius=12):
c.setFillColor(fill_col)
c.roundRect(x, y, w, h, radius, fill=1, stroke=0)
def icon_circle(c, cx_, cy_, r, bg_col, char, char_size=28, fg=WHITE):
c.setFillColor(bg_col)
c.circle(cx_, cy_, r, fill=1, stroke=0)
c.setFillColor(fg)
c.setFont("Helvetica-Bold", char_size)
c.drawCentredString(cx_, cy_ - char_size * 0.35, char)
def center_text(c, text, y, font, size, col):
c.setFillColor(col)
c.setFont(font, size)
c.drawCentredString(cx(), y, text)
def wrap_text(c, text, x, y, max_width, font, size, col, line_height=None):
"""Simple word-wrap. Returns final y."""
if line_height is None:
line_height = size * 1.45
c.setFillColor(col)
c.setFont(font, size)
words = text.split()
line = ""
for word in words:
test = (line + " " + word).strip()
if c.stringWidth(test, font, size) <= max_width:
line = test
else:
c.drawString(x, y, line)
y -= line_height
line = word
if line:
c.drawString(x, y, line)
y -= line_height
return y
def center_wrap(c, text, y, max_width, font, size, col, line_height=None):
"""Centered word-wrap. Returns final y."""
if line_height is None:
line_height = size * 1.45
c.setFillColor(col)
c.setFont(font, size)
words = text.split()
line = ""
lines_out = []
for word in words:
test = (line + " " + word).strip()
if c.stringWidth(test, font, size) <= max_width:
line = test
else:
lines_out.append(line)
line = word
if line:
lines_out.append(line)
for ln in lines_out:
c.drawCentredString(cx(), y, ln)
y -= line_height
return y
def dot_nav(c, total, current, y=38, dot_r=7, gap=22, active_col=WHITE, inactive_col=None):
"""Draw slide indicator dots."""
if inactive_col is None:
inactive_col = HexColor("#FFFFFF55")
total_w = (total - 1) * gap
start_x = cx() - total_w / 2
for i in range(total):
xd = start_x + i * gap
c.setFillColor(active_col if i == current else inactive_col)
c.circle(xd, y, dot_r if i == current else 5, fill=1, stroke=0)
def tag_pill(c, text, x, y, bg, fg=WHITE, font_size=18, pad_x=20, pad_h=14):
w = c.stringWidth(text, "Helvetica-Bold", font_size) + pad_x * 2
h = font_size + pad_h
c.setFillColor(bg)
c.roundRect(x - w / 2, y - h / 2, w, h, h / 2, fill=1, stroke=0)
c.setFillColor(fg)
c.setFont("Helvetica-Bold", font_size)
c.drawCentredString(x, y - font_size * 0.35, text)
def bullet_rows(c, items, x, y, icon_char, icon_bg, font_size=22,
icon_r=22, gap=18, max_w=None, line_h=None):
"""Draw icon + text bullet rows. Returns final y."""
if max_w is None:
max_w = PT_W - x - 40
if line_h is None:
line_h = font_size * 1.5
for item in items:
icon_circle(c, x + icon_r, y - icon_r + 8, icon_r, icon_bg, icon_char, char_size=font_size - 4)
c.setFillColor(GREY_DARK)
c.setFont("Helvetica", font_size)
text_x = x + icon_r * 2 + 14
# quick wrap
words = item.split()
line = ""
first = True
ty = y
for word in words:
test = (line + " " + word).strip()
if c.stringWidth(test, "Helvetica", font_size) <= max_w - icon_r * 2 - 14:
line = test
else:
c.drawString(text_x, ty, line)
ty -= line_h * 0.85
line = word
first = False
if line:
c.drawString(text_x, ty, line)
y -= max(line_h, (ty - y) * -1 + line_h * 0.4)
y -= gap
return y
# ═══════════════════════════════════════════════════════════════════════════════
# SLIDE 1 – COVER
# ═══════════════════════════════════════════════════════════════════════════════
draw_full_bg(c, TEAL_DARK, TEAL2)
# Big rain-drop decorative circles
c.setFillColor(HexColor("#FFFFFF08"))
c.circle(PT_W * 0.85, PT_H * 0.78, 180, fill=1, stroke=0)
c.circle(PT_W * 0.15, PT_H * 0.22, 130, fill=1, stroke=0)
c.circle(PT_W * 0.5, PT_H * 0.55, 260, fill=1, stroke=0)
# Top label pill
tag_pill(c, "MONSOON SPECIAL", cx(), PT_H - 160, ORANGE, WHITE, 22)
# Main icon
icon_circle(c, cx(), PT_H * 0.56, 100, HexColor("#FFFFFF20"), "🎧", 72, WHITE)
# Title
c.setFillColor(WHITE)
c.setFont("Helvetica-Bold", 70)
c.drawCentredString(cx(), PT_H * 0.42, "Hearing Aid")
c.setFont("Helvetica-Bold", 62)
c.drawCentredString(cx(), PT_H * 0.42 - 78, "Monsoon Care")
# Subtitle
c.setFillColor(TEAL_LIGHT)
c.setFont("Helvetica", 30)
c.drawCentredString(cx(), PT_H * 0.42 - 140, "Battery & Rechargeable")
# Divider
c.setStrokeColor(HexColor("#FFFFFF40"))
c.setLineWidth(1.5)
c.line(cx() - 160, PT_H * 0.32, cx() + 160, PT_H * 0.32)
# CTA
c.setFillColor(WHITE)
c.setFont("Helvetica-Oblique", 26)
c.drawCentredString(cx(), PT_H * 0.28, "Swipe to protect your device ›")
# Bottom brand strip
c.setFillColor(HexColor("#00000040"))
c.rect(0, 0, PT_W, 90, fill=1, stroke=0)
c.setFillColor(WHITE)
c.setFont("Helvetica-Bold", 22)
c.drawCentredString(cx(), 32, "Save this guide · Share with family")
dot_nav(c, 9, 0)
c.showPage()
# ═══════════════════════════════════════════════════════════════════════════════
# SLIDE 2 – WHY MONSOON IS RISKY
# ═══════════════════════════════════════════════════════════════════════════════
draw_full_bg(c, HexColor("#0B3D52"), TEAL_DARK)
# Top label
tag_pill(c, "⚠ THE RISK", cx(), PT_H - 130, RED, WHITE, 22)
center_text(c, "Why Monsoon Is Dangerous", PT_H - 220, "Helvetica-Bold", 46, WHITE)
center_text(c, "for Your Hearing Aids", PT_H - 280, "Helvetica-Bold", 46, WHITE)
# Risk cards (5 cards stacked)
risks = [
(RED, "💧", "Corrosion", "Moisture corrodes internal circuits permanently"),
(ORANGE, "🔇", "Blocked Ports", "Water clogs microphone & receiver openings"),
(YELLOW, "🔋", "Battery Drain", "Humidity cuts battery life by up to 50%"),
(GREEN, "🦠", "Fungal Growth", "Mould grows inside tubing & ear moulds"),
(TEAL2, "🌡", "Condensation", "AC ↔ humid air creates condensation inside"),
]
card_h = 148
card_w = PT_W - 80
card_x = 40
start_y = PT_H - 360
for i, (col, emoji, title_, desc) in enumerate(risks):
y0 = start_y - i * (card_h + 14)
# card bg
c.setFillColor(HexColor("#FFFFFF10"))
c.roundRect(card_x, y0 - card_h, card_w, card_h, 16, fill=1, stroke=0)
# left accent strip
c.setFillColor(col)
c.roundRect(card_x, y0 - card_h, 8, card_h, 4, fill=1, stroke=0)
# emoji
c.setFont("Helvetica", 38)
c.setFillColor(WHITE)
c.drawString(card_x + 26, y0 - card_h + card_h / 2 - 10, emoji)
# title
c.setFillColor(col)
c.setFont("Helvetica-Bold", 26)
c.drawString(card_x + 84, y0 - card_h + card_h / 2 + 14, title_)
# desc
c.setFillColor(HexColor("#DDDDDD"))
c.setFont("Helvetica", 21)
c.drawString(card_x + 84, y0 - card_h + card_h / 2 - 20, desc)
dot_nav(c, 9, 1)
c.showPage()
# ═══════════════════════════════════════════════════════════════════════════════
# SLIDE 3 – GENERAL RULES (BOTH TYPES)
# ═══════════════════════════════════════════════════════════════════════════════
draw_full_bg(c, HexColor("#F8FEFF"), HexColor("#E0F4FA"))
tag_pill(c, "✅ BOTH AID TYPES", cx(), PT_H - 120, TEAL, WHITE, 22)
center_text(c, "General Monsoon Rules", PT_H - 210, "Helvetica-Bold", 50, TEAL_DARK)
center_text(c, "Apply to ALL hearing aids", PT_H - 268, "Helvetica", 28, GREY_MID)
rules = [
("🧹", "Wipe aids daily with a dry lint-free cloth"),
("🖌", "Brush microphone ports every evening"),
("🚫", "Never store in the bathroom"),
("🧤", "Use a moisture-resistant protective sleeve"),
("☂", "Always wear a hat or carry an umbrella"),
("❌", "Remove before heavy rain or exercise"),
("💆", "Apply hairspray/sunscreen BEFORE inserting"),
("👨⚕️", "Professional clean — start & mid monsoon"),
]
row_h = 96
row_w = PT_W - 80
start_y = PT_H - 320
for i, (emoji, text_) in enumerate(rules):
y0 = start_y - i * (row_h + 8)
bg = TEAL_LIGHT if i % 2 == 0 else WHITE
c.setFillColor(bg)
c.roundRect(40, y0 - row_h, row_w, row_h, 10, fill=1, stroke=0)
c.setFont("Helvetica", 34)
c.setFillColor(TEAL)
c.drawString(60, y0 - row_h + row_h / 2 - 12, emoji)
c.setFillColor(GREY_DARK)
c.setFont("Helvetica", 22)
c.drawString(116, y0 - row_h + row_h / 2 - 10, text_)
dot_nav(c, 9, 2, active_col=TEAL, inactive_col=HexColor("#0B6E8C55"))
c.showPage()
# ═══════════════════════════════════════════════════════════════════════════════
# SLIDE 4 – BATTERY-OPERATED CARE
# ═══════════════════════════════════════════════════════════════════════════════
draw_full_bg(c, HexColor("#3D1C00"), ORANGE)
# Decorative circles
c.setFillColor(HexColor("#FFFFFF08"))
c.circle(PT_W * 0.9, PT_H * 0.2, 200, fill=1, stroke=0)
c.circle(PT_W * 0.1, PT_H * 0.8, 150, fill=1, stroke=0)
tag_pill(c, "🔋 BATTERY-OPERATED AIDS", cx(), PT_H - 130, HexColor("#FFFFFF30"), WHITE, 20)
center_text(c, "Nightly Battery Care", PT_H - 220, "Helvetica-Bold", 52, WHITE)
steps = [
("1", "Open battery door FULLY every night",
"Allows air circulation, prevents moisture trapping"),
("2", "Use a drying box for 6–8 hours",
"Electronic or desiccant — gold standard overnight"),
("3", "Store spare batteries in airtight container",
"With silica gel; never in a damp pocket or bag"),
("4", "Change batteries every 5–6 days",
"Humidity shortens life vs. normal 7–10 days"),
("5", "Check contacts for corrosion",
"White/green residue → clean with dry cotton swab"),
]
card_h = 160
card_w = PT_W - 80
sy = PT_H - 310
for i, (num, title_, sub) in enumerate(steps):
y0 = sy - i * (card_h + 12)
c.setFillColor(HexColor("#FFFFFF15"))
c.roundRect(40, y0 - card_h, card_w, card_h, 14, fill=1, stroke=0)
# number circle
c.setFillColor(ORANGE)
c.circle(82, y0 - card_h / 2, 28, fill=1, stroke=0)
c.setFillColor(WHITE)
c.setFont("Helvetica-Bold", 24)
c.drawCentredString(82, y0 - card_h / 2 - 9, num)
# text
c.setFillColor(WHITE)
c.setFont("Helvetica-Bold", 24)
c.drawString(124, y0 - card_h / 2 + 12, title_)
c.setFillColor(ORANGE_L)
c.setFont("Helvetica", 20)
c.drawString(124, y0 - card_h / 2 - 18, sub)
dot_nav(c, 9, 3)
c.showPage()
# ═══════════════════════════════════════════════════════════════════════════════
# SLIDE 5 – RECHARGEABLE CARE
# ═══════════════════════════════════════════════════════════════════════════════
draw_full_bg(c, HexColor("#0A2E1A"), GREEN)
c.setFillColor(HexColor("#FFFFFF08"))
c.circle(PT_W * 0.85, PT_H * 0.25, 180, fill=1, stroke=0)
c.circle(PT_W * 0.1, PT_H * 0.75, 140, fill=1, stroke=0)
tag_pill(c, "⚡ RECHARGEABLE AIDS", cx(), PT_H - 130, HexColor("#FFFFFF25"), WHITE, 20)
center_text(c, "Rechargeable Care", PT_H - 218, "Helvetica-Bold", 52, WHITE)
# Key callout
c.setFillColor(HexColor("#FFFFFF20"))
c.roundRect(40, PT_H - 326, PT_W - 80, 76, 12, fill=1, stroke=0)
c.setFillColor(YELLOW)
c.setFont("Helvetica-Bold", 21)
c.drawCentredString(cx(), PT_H - 292, "⚠ Cannot open battery compartment!")
c.setFillColor(WHITE)
c.setFont("Helvetica", 20)
c.drawCentredString(cx(), PT_H - 316, "Nightly drying routine is CRITICAL")
steps_r = [
("1", "Power OFF before charging", "Hold power button until it turns off"),
("2", "Wipe charging contacts dry", "Metal contacts must be moisture-free"),
("3", "Use charging case overnight", "Many Phonak/ReSound cases have built-in dryer"),
("4", "Separate dryer if no built-in", "30–60 min in dryer box before charger"),
("5", "Never charge while wet", "Moisture on contacts = short circuit risk"),
]
card_h = 148
sy = PT_H - 430
for i, (num, title_, sub) in enumerate(steps_r):
y0 = sy - i * (card_h + 12)
c.setFillColor(HexColor("#FFFFFF15"))
c.roundRect(40, y0 - card_h, PT_W - 80, card_h, 14, fill=1, stroke=0)
c.setFillColor(GREEN)
c.circle(80, y0 - card_h / 2, 26, fill=1, stroke=0)
c.setFillColor(WHITE)
c.setFont("Helvetica-Bold", 22)
c.drawCentredString(80, y0 - card_h / 2 - 8, num)
c.setFillColor(WHITE)
c.setFont("Helvetica-Bold", 23)
c.drawString(120, y0 - card_h / 2 + 12, title_)
c.setFillColor(GREEN_L)
c.setFont("Helvetica", 19)
c.drawString(120, y0 - card_h / 2 - 16, sub)
dot_nav(c, 9, 4)
c.showPage()
# ═══════════════════════════════════════════════════════════════════════════════
# SLIDE 6 – DRYING TOOLS
# ═══════════════════════════════════════════════════════════════════════════════
draw_full_bg(c, HexColor("#F9F9FF"), HexColor("#EAF4FF"))
tag_pill(c, "🔧 DRYING TOOLS", cx(), PT_H - 120, TEAL, WHITE, 22)
center_text(c, "Which Dryer Should You Use?", PT_H - 210, "Helvetica-Bold", 46, TEAL_DARK)
tools = [
(TEAL, "🫙", "Silica Gel Jar", "Passive drying · Budget option · Replace monthly", "Both"),
(ORANGE, "🔌", "Electronic Drying Box", "Best daily dryer · 6–8 hrs overnight · Gold standard", "Both"),
(YELLOW, "☀", "UV-C Drying Box", "Kills bacteria + fungi · Best for monsoon season", "Both"),
(GREEN, "⚡", "Charging Case w/ Dryer", "Dries while charging · Check your model specs", "Rechargeable"),
]
card_h = 170
card_w = PT_W - 80
sy = PT_H - 300
for i, (col, emoji, name, desc, for_) in enumerate(tools):
y0 = sy - i * (card_h + 16)
# Shadow
c.setFillColor(HexColor("#00000012"))
c.roundRect(44, y0 - card_h - 4, card_w, card_h, 16, fill=1, stroke=0)
# Card
c.setFillColor(WHITE)
c.roundRect(40, y0 - card_h, card_w, card_h, 16, fill=1, stroke=0)
# Left color bar
c.setFillColor(col)
c.roundRect(40, y0 - card_h, 10, card_h, 4, fill=1, stroke=0)
# Emoji bg circle
c.setFillColor(HexColor(col.hexval() + "22"))
c.circle(98, y0 - card_h / 2 + 4, 34, fill=1, stroke=0)
c.setFont("Helvetica", 34)
c.setFillColor(col)
c.drawCentredString(98, y0 - card_h / 2 - 7, emoji)
# Name
c.setFillColor(GREY_DARK)
c.setFont("Helvetica-Bold", 25)
c.drawString(148, y0 - card_h / 2 + 26, name)
# Desc
c.setFillColor(GREY_MID)
c.setFont("Helvetica", 19)
c.drawString(148, y0 - card_h / 2 + 2, desc)
# For pill
pill_col = GREEN if for_ == "Both" else TEAL
pill_text = "✓ Both types" if for_ == "Both" else "✓ Rechargeable"
pill_w = c.stringWidth(pill_text, "Helvetica-Bold", 17) + 20
c.setFillColor(pill_col)
c.roundRect(148, y0 - card_h / 2 - 26, pill_w, 28, 6, fill=1, stroke=0)
c.setFillColor(WHITE)
c.setFont("Helvetica-Bold", 17)
c.drawString(158, y0 - card_h / 2 - 16, pill_text)
dot_nav(c, 9, 5, active_col=TEAL, inactive_col=HexColor("#0B6E8C55"))
c.showPage()
# ═══════════════════════════════════════════════════════════════════════════════
# SLIDE 7 – EMERGENCY AID GOT WET
# ═══════════════════════════════════════════════════════════════════════════════
draw_full_bg(c, HexColor("#2D0000"), RED)
c.setFillColor(HexColor("#FFFFFF08"))
c.circle(PT_W * 0.85, PT_H * 0.85, 220, fill=1, stroke=0)
tag_pill(c, "🚨 EMERGENCY GUIDE", cx(), PT_H - 130, HexColor("#FFFFFF30"), WHITE, 20)
center_text(c, "Your Aid Got Wet!", PT_H - 210, "Helvetica-Bold", 58, WHITE)
center_text(c, "Act immediately — follow these steps", PT_H - 272, "Helvetica", 26, HexColor("#FFCCCC"))
steps_e = [
"Remove from water IMMEDIATELY",
"Power OFF (hold power button for rechargeable)",
"Do NOT press other buttons",
"Wipe exterior with dry lint-free cloth",
"Battery aid: remove battery, open door",
"Place in drying box / desiccant jar",
"Rechargeable: don't charge until fully dry",
"Do NOT use rice, hairdryer or oven",
"Still fails after 48 hrs? Call audiologist",
]
row_h = 90
sy = PT_H - 340
for i, step in enumerate(steps_e):
y0 = sy - i * (row_h + 6)
c.setFillColor(HexColor("#FFFFFF12"))
c.roundRect(40, y0 - row_h, PT_W - 80, row_h, 10, fill=1, stroke=0)
# Number badge
c.setFillColor(RED)
c.setStrokeColor(WHITE)
c.setLineWidth(1.5)
c.circle(74, y0 - row_h / 2, 20, fill=1, stroke=1)
c.setLineWidth(1)
c.setFillColor(WHITE)
c.setFont("Helvetica-Bold", 18)
c.drawCentredString(74, y0 - row_h / 2 - 7, str(i + 1))
# Text
c.setFillColor(WHITE)
c.setFont("Helvetica", 21)
c.drawString(106, y0 - row_h / 2 - 8, step)
dot_nav(c, 9, 6)
c.showPage()
# ═══════════════════════════════════════════════════════════════════════════════
# SLIDE 8 – DAILY CHECKLIST
# ═══════════════════════════════════════════════════════════════════════════════
draw_full_bg(c, HexColor("#F0FFF4"), HexColor("#C8EDD8"))
tag_pill(c, "📋 DAILY CHECKLIST", cx(), PT_H - 120, GREEN, WHITE, 22)
center_text(c, "Tick These Off Every Day", PT_H - 208, "Helvetica-Bold", 48, HexColor("#1B4332"))
center_text(c, "Screenshot & save this slide!", PT_H - 260, "Helvetica-Oblique", 26, GREY_MID)
checks = [
("Wipe aids with dry cloth", "Both"),
("Brush microphone & sound ports", "Both"),
("Open battery door fully at night", "Battery"),
("Wipe charging contacts before charger","Rechargeable"),
("Place in drying box / charging case", "Both"),
("Check/replace battery if weak", "Battery"),
("Spare batteries in airtight container","Battery"),
("Wore protective sleeve outdoors", "Both"),
("Did NOT store in bathroom", "Both"),
("Skipped hairspray while wearing aids", "Both"),
]
row_h = 100
sy = PT_H - 310
for i, (task, type_) in enumerate(checks):
y0 = sy - i * (row_h + 8)
bg = HexColor("#FFFFFF") if i % 2 == 0 else HexColor("#E8F5E9")
c.setFillColor(bg)
c.roundRect(40, y0 - row_h, PT_W - 80, row_h, 10, fill=1, stroke=0)
# Checkbox
c.setStrokeColor(GREEN)
c.setLineWidth(2)
c.roundRect(60, y0 - row_h + row_h/2 - 18, 36, 36, 4, fill=0, stroke=1)
# Type badge
badge_col = ORANGE if type_ == "Battery" else (TEAL if type_ == "Rechargeable" else GREEN)
bw = c.stringWidth(type_, "Helvetica-Bold", 15) + 12
c.setFillColor(badge_col)
c.roundRect(PT_W - 60 - bw, y0 - row_h + row_h/2 - 12, bw, 26, 5, fill=1, stroke=0)
c.setFillColor(WHITE)
c.setFont("Helvetica-Bold", 15)
c.drawString(PT_W - 54 - bw, y0 - row_h + row_h/2 - 4, type_)
# Task text
c.setFillColor(GREY_DARK)
c.setFont("Helvetica", 21)
c.drawString(110, y0 - row_h + row_h/2 - 9, task)
dot_nav(c, 9, 7, active_col=GREEN, inactive_col=HexColor("#2E8B5755"))
c.showPage()
# ═══════════════════════════════════════════════════════════════════════════════
# SLIDE 9 – CLOSING / CTA
# ═══════════════════════════════════════════════════════════════════════════════
draw_full_bg(c, TEAL_DARK, HexColor("#0E3A50"))
c.setFillColor(HexColor("#FFFFFF08"))
c.circle(PT_W * 0.5, PT_H * 0.55, 320, fill=1, stroke=0)
c.circle(PT_W * 0.1, PT_H * 0.1, 100, fill=1, stroke=0)
c.circle(PT_W * 0.9, PT_H * 0.9, 130, fill=1, stroke=0)
# Big tick
icon_circle(c, cx(), PT_H * 0.62, 110, HexColor("#FFFFFF18"), "✓", 90, WHITE)
center_text(c, "You're All Set!", PT_H * 0.47, "Helvetica-Bold", 60, WHITE)
tips_final = [
"5 mins of nightly care = months of extra life",
"Share this guide with every family member",
"Book a mid-monsoon audiologist check-up",
"When in doubt, see your audiologist!",
]
sy = PT_H * 0.38
for tip in tips_final:
center_text(c, "• " + tip, sy, "Helvetica", 24, TEAL_LIGHT)
sy -= 50
# Divider
c.setStrokeColor(HexColor("#FFFFFF30"))
c.setLineWidth(1)
c.line(cx() - 200, sy - 20, cx() + 200, sy - 20)
# Save + Share CTA
c.setFillColor(ORANGE)
c.roundRect(cx() - 240, sy - 120, 480, 72, 36, fill=1, stroke=0)
c.setFillColor(WHITE)
c.setFont("Helvetica-Bold", 28)
c.drawCentredString(cx(), sy - 88, "💾 Save · 🔁 Share · 🔔 Follow")
# Bottom note
c.setFillColor(HexColor("#AADDEE"))
c.setFont("Helvetica", 20)
c.drawCentredString(cx(), 80, "Guide by Orris · July 2026")
c.setFont("Helvetica", 17)
c.drawCentredString(cx(), 52, "Consult your audiologist for personalised advice")
dot_nav(c, 9, 8)
c.showPage()
# ── Save ──────────────────────────────────────────────────────────────────────
c.save()
print(f"Reel PDF saved: {out_path}")
cd /tmp/workspace/hearing-aid-guide && python create_reel.py 2>&1
ls -lh /tmp/workspace/hearing-aid-guide/
Hearing Aid Monsoon Reel
PDF Document · PDF
| Slide | Title | Theme |
|---|---|---|
| 1 | Cover | Dark teal gradient, title + "Swipe to protect your device ›" hook |
| 2 | Why Monsoon Is Dangerous | 5 colour-coded risk cards (corrosion, blocked ports, battery drain, fungal growth, condensation) |
| 3 | General Rules | 8 alternating-row checklist tiles — applies to all aids |
| 4 | Battery-Operated Care | Orange theme, 5 numbered step cards with nightly routine |
| 5 | Rechargeable Care | Green theme, callout banner + 5 nightly steps |
| 6 | Drying Tools Guide | White card layout comparing 4 dryer types with "Both/Rechargeable" badges |
| 7 | Emergency: Aid Got Wet | Red theme, 9-step numbered emergency action list |
| 8 | Daily Checklist | Green theme, 10 tick-box rows with Battery/Rechargeable/Both badges |
| 9 | Closing CTA | Teal + save/share/follow button |
Can you create a reel acc to this
Tell me your preferred reel style (minimalist, animated, branded, etc.)
~/hearing-aid-guide/create_branded_reel.py
"""
Branded Clinical Carousel – Hearing Aid Monsoon Care
9 slides · 1080 × 1920 px · Portrait · Professional teal/white palette
Includes clinic name placeholder on every slide.
"""
from reportlab.pdfgen import canvas
from reportlab.lib.colors import HexColor, white, black, Color
from reportlab.lib.units import mm
# ── Canvas dimensions (1080×1920 @ 96dpi → points) ──────────────────────────
W = 1080 * 72 / 96 # 810 pt
H = 1920 * 72 / 96 # 1440 pt
# ── Brand palette ─────────────────────────────────────────────────────────────
NAVY = HexColor("#0A2540") # deep navy – header bars
TEAL = HexColor("#0B7285") # primary teal
TEAL_MED = HexColor("#1A9EBA") # medium teal – accents
TEAL_PALE = HexColor("#E8F7FA") # very pale teal – card fills
SKY = HexColor("#B2E0EC") # sky – decorative
SLATE = HexColor("#3D5A6C") # slate – body text
WHITE_PURE = white
OFF_WHITE = HexColor("#F7FBFD")
ORANGE = HexColor("#D95F00") # warm orange – battery aid accent
ORANGE_PALE = HexColor("#FFF0E6")
GREEN = HexColor("#1A7A4A") # clinical green – rechargeable accent
GREEN_PALE = HexColor("#E8F5EE")
RED = HexColor("#B02020") # alert red
RED_PALE = HexColor("#FDF0F0")
GOLD = HexColor("#C89B14") # gold – star/highlight
MID_GREY = HexColor("#8AA0AE")
RULE = HexColor("#D0E8EF") # thin rule lines
CLINIC_NAME = "[Your Clinic Name]"
# ── Helpers ───────────────────────────────────────────────────────────────────
def cx(): return W / 2
def set_font(c, name, size, color=None):
c.setFont(name, size)
if color:
c.setFillColor(color)
def draw_text_centered(c, text, y, font, size, color):
c.setFont(font, size)
c.setFillColor(color)
c.drawCentredString(cx(), y, text)
def draw_text_left(c, text, x, y, font, size, color):
c.setFont(font, size)
c.setFillColor(color)
c.drawString(x, y, text)
def pill_tag(c, text, x, y, bg, fg=white, font_size=17, rx=12):
c.setFont("Helvetica-Bold", font_size)
tw = c.stringWidth(text, "Helvetica-Bold", font_size)
pw = tw + 28
ph = font_size + 16
c.setFillColor(bg)
c.roundRect(x - pw / 2, y - ph / 2, pw, ph, rx, fill=1, stroke=0)
c.setFillColor(fg)
c.drawCentredString(x, y - font_size * 0.36, text)
def horiz_rule(c, y, color=RULE, lw=1):
c.setStrokeColor(color)
c.setLineWidth(lw)
c.line(48, y, W - 48, y)
def dot_row(c, total, active, y=34, r_active=8, r_dot=5, gap=24,
col_active=TEAL, col_dot=None):
if col_dot is None: col_dot = HexColor("#0B728540")
total_w = (total - 1) * gap
x0 = cx() - total_w / 2
for i in range(total):
c.setFillColor(col_active if i == active else col_dot)
c.circle(x0 + i * gap,
y,
r_active if i == active else r_dot,
fill=1, stroke=0)
# ── Per-slide frame (header + footer shared structure) ────────────────────────
def draw_frame(c, slide_num, total=9, header_bg=NAVY, show_clinic=True):
"""
Header bar (top 110 pt)
Footer bar (bottom 80 pt)
"""
# ── Header ────────────────────────────────────────────────────────────────
c.setFillColor(header_bg)
c.rect(0, H - 110, W, 110, fill=1, stroke=0)
# Teal accent line below header
c.setFillColor(TEAL_MED)
c.rect(0, H - 118, W, 8, fill=1, stroke=0)
# Clinic name (left of header)
if show_clinic:
c.setFont("Helvetica-Bold", 21)
c.setFillColor(SKY)
c.drawString(44, H - 74, CLINIC_NAME)
# Slide counter (right of header)
c.setFont("Helvetica", 19)
c.setFillColor(HexColor("#AACEDD"))
counter = f"{slide_num} / {total}"
c.drawRightString(W - 44, H - 74, counter)
# ── Footer ────────────────────────────────────────────────────────────────
c.setFillColor(NAVY)
c.rect(0, 0, W, 80, fill=1, stroke=0)
c.setFillColor(TEAL_MED)
c.rect(0, 76, W, 4, fill=1, stroke=0)
# Footer text
c.setFont("Helvetica", 17)
c.setFillColor(HexColor("#7FAABD"))
c.drawCentredString(cx(), 28, "Consult your audiologist for personalised advice")
dot_row(c, total, slide_num - 1, y=56)
def card(c, x, y, w, h, fill=TEAL_PALE, stroke_col=RULE, radius=14, stroke_w=1):
"""Draw a rounded card."""
c.setFillColor(fill)
c.setStrokeColor(stroke_col)
c.setLineWidth(stroke_w)
c.roundRect(x, y, w, h, radius, fill=1, stroke=1)
def left_accent_card(c, x, y, w, h, accent_col, fill=WHITE_PURE, radius=12):
"""Card with a 6px left colour accent bar."""
card(c, x, y, w, h, fill=fill, stroke_col=RULE)
c.setFillColor(accent_col)
c.roundRect(x, y, 8, h, 4, fill=1, stroke=0)
def icon_badge(c, x, y, r, bg, text, font_size=30, fg=WHITE_PURE):
c.setFillColor(bg)
c.circle(x, y, r, fill=1, stroke=0)
c.setFillColor(fg)
c.setFont("Helvetica-Bold", font_size)
c.drawCentredString(x, y - font_size * 0.36, text)
# ── Word-wrap helpers ─────────────────────────────────────────────────────────
def multiline_centered(c, text, y, max_w, font, size, color, lh=None):
if lh is None: lh = size * 1.5
c.setFont(font, size); c.setFillColor(color)
words = text.split(); line = ""; lines = []
for w_ in words:
t = (line + " " + w_).strip()
if c.stringWidth(t, font, size) <= max_w: line = t
else: lines.append(line); line = w_
if line: lines.append(line)
for ln in lines:
c.drawCentredString(cx(), y, ln); y -= lh
return y
def multiline_left(c, text, x, y, max_w, font, size, color, lh=None):
if lh is None: lh = size * 1.5
c.setFont(font, size); c.setFillColor(color)
words = text.split(); line = ""; lines = []
for w_ in words:
t = (line + " " + w_).strip()
if c.stringWidth(t, font, size) <= max_w: line = t
else: lines.append(line); line = w_
if line: lines.append(line)
for ln in lines:
c.drawString(x, y, ln); y -= lh
return y
# ═══════════════════════════════════════════════════════════════════════════════
# BUILD PDF
# ═══════════════════════════════════════════════════════════════════════════════
out = "/tmp/workspace/hearing-aid-guide/Hearing_Aid_Monsoon_Branded_Reel.pdf"
cv = canvas.Canvas(out, pagesize=(W, H))
# ─────────────────────────────────────────────────────────────────────────────
# SLIDE 1 – COVER
# ─────────────────────────────────────────────────────────────────────────────
# Full background
cv.setFillColor(OFF_WHITE)
cv.rect(0, 0, W, H, fill=1, stroke=0)
# Top navy header
draw_frame(cv, 1, show_clinic=True)
# Decorative teal arch shape behind centre
cv.setFillColor(HexColor("#0B728510"))
cv.ellipse(cx() - 310, H * 0.25, cx() + 310, H * 0.78, fill=1, stroke=0)
# Monsoon season badge
pill_tag(cv, " MONSOON SEASON GUIDE ", cx(), H - 170, TEAL, WHITE_PURE, 20)
# Central icon circle
cv.setFillColor(TEAL_PALE)
cv.circle(cx(), H * 0.56, 130, fill=1, stroke=0)
cv.setFillColor(TEAL_MED)
cv.circle(cx(), H * 0.56, 100, fill=1, stroke=0)
cv.setFillColor(WHITE_PURE)
cv.setFont("Helvetica", 80)
cv.drawCentredString(cx(), H * 0.56 - 28, "🎧")
# Title block
draw_text_centered(cv, "Hearing Aid", H * 0.41, "Helvetica-Bold", 66, NAVY)
draw_text_centered(cv, "Monsoon Care", H * 0.41 - 76, "Helvetica-Bold", 66, TEAL)
# Divider with teal circle ornaments
cv.setStrokeColor(RULE); cv.setLineWidth(1.5)
cv.line(80, H * 0.41 - 108, W - 80, H * 0.41 - 108)
cv.setFillColor(TEAL_MED)
cv.circle(80, H * 0.41 - 108, 5, fill=1, stroke=0)
cv.circle(W - 80, H * 0.41 - 108, 5, fill=1, stroke=0)
# Subtitle
draw_text_centered(cv, "Battery-Operated & Rechargeable", H * 0.41 - 150,
"Helvetica", 28, SLATE)
# Two feature pills
pill_tag(cv, "🔋 Battery-Operated", cx() - 148, H * 0.26, ORANGE_PALE,
ORANGE, 19)
pill_tag(cv, "⚡ Rechargeable", cx() + 158, H * 0.26, GREEN_PALE, GREEN, 19)
# Swipe CTA
draw_text_centered(cv, "Swipe to learn how to protect your device ›",
H * 0.19, "Helvetica-Oblique", 24, TEAL_MED)
cv.showPage()
# ─────────────────────────────────────────────────────────────────────────────
# SLIDE 2 – WHY MONSOON IS RISKY
# ─────────────────────────────────────────────────────────────────────────────
cv.setFillColor(OFF_WHITE); cv.rect(0, 0, W, H, fill=1, stroke=0)
draw_frame(cv, 2)
# Section title area
cv.setFillColor(TEAL_PALE)
cv.roundRect(40, H - 270, W - 80, 130, 14, fill=1, stroke=0)
cv.setFillColor(TEAL)
cv.roundRect(40, H - 270, 6, 130, 3, fill=1, stroke=0)
draw_text_centered(cv, "Why Monsoon Is Dangerous", H - 190, "Helvetica-Bold", 42, NAVY)
draw_text_centered(cv, "5 threats to your hearing aid", H - 240, "Helvetica", 26, SLATE)
# Risk cards
risks = [
(RED, "💧", "Corrosion",
"Moisture corrodes internal circuits & contacts, causing permanent failure"),
(ORANGE, "🔇", "Blocked Ports",
"Water clogs microphone & receiver openings, muffling sound"),
(GOLD, "🔋", "Battery Drain",
"Humidity shortens disposable battery life by up to 50%"),
(GREEN, "🦠", "Fungal Growth",
"Persistent moisture breeds mould inside tubing & ear moulds"),
(TEAL, "🌡", "Condensation",
"Moving AC ↔ humid air creates condensation inside the shell"),
]
card_h = 154; gap = 12
start_y = H - 310
for i, (accent, emoji, title_, desc) in enumerate(risks):
y0 = start_y - i * (card_h + gap)
left_accent_card(cv, 40, y0 - card_h, W - 80, card_h, accent)
# Emoji in tinted circle
cv.setFillColor(HexColor(accent.hexval() + "22"))
cv.circle(96, y0 - card_h / 2, 34, fill=1, stroke=0)
cv.setFont("Helvetica", 32); cv.setFillColor(accent)
cv.drawCentredString(96, y0 - card_h / 2 - 11, emoji)
# Title
draw_text_left(cv, title_, 148, y0 - card_h / 2 + 20, "Helvetica-Bold", 27, NAVY)
# Desc (wrapped)
multiline_left(cv, desc, 148, y0 - card_h / 2 - 8,
W - 80 - 148 - 20, "Helvetica", 20, SLATE, lh=26)
cv.showPage()
# ─────────────────────────────────────────────────────────────────────────────
# SLIDE 3 – GENERAL RULES
# ─────────────────────────────────────────────────────────────────────────────
cv.setFillColor(OFF_WHITE); cv.rect(0, 0, W, H, fill=1, stroke=0)
draw_frame(cv, 3)
cv.setFillColor(TEAL_PALE)
cv.roundRect(40, H - 260, W - 80, 118, 14, fill=1, stroke=0)
cv.setFillColor(TEAL); cv.roundRect(40, H - 260, 6, 118, 3, fill=1, stroke=0)
draw_text_centered(cv, "General Care Rules", H - 186, "Helvetica-Bold", 44, NAVY)
draw_text_centered(cv, "Applies to ALL hearing aids — both types", H - 232,
"Helvetica", 24, SLATE)
rules = [
("01", "Daily Wipe-Down",
"Wipe shell, mic ports & receiver with a soft dry lint-free cloth each evening"),
("02", "Brush the Ports",
"Use your cleaning brush on mic openings daily to clear moisture & debris"),
("03", "No Bathroom Storage",
"Never store aids in the bathroom — highest-humidity room in the house"),
("04", "Protective Sleeve",
"Use a moisture-resistant hearing aid sleeve when outdoors in humid weather"),
("05", "Hat / Umbrella Rule",
"Always cover ears in rain — direct rain harms even IP-rated devices"),
("06", "Remove Before Rain",
"Remove aids before heavy rain, steam rooms or intense exercise"),
("07", "Products First",
"Apply hairspray, sunscreen or insect repellent BEFORE inserting aids"),
("08", "Professional Check",
"Schedule a clean at the start of monsoon and once mid-season"),
]
card_h = 118; gap = 9; start_y = H - 296
for i, (num, title_, desc) in enumerate(rules):
y0 = start_y - i * (card_h + gap)
bg = TEAL_PALE if i % 2 == 0 else WHITE_PURE
card(cv, 40, y0 - card_h, W - 80, card_h, fill=bg, stroke_col=RULE, radius=10)
# Number badge
cv.setFillColor(TEAL_MED)
cv.roundRect(54, y0 - card_h + card_h / 2 - 16, 44, 32, 6, fill=1, stroke=0)
cv.setFillColor(WHITE_PURE); cv.setFont("Helvetica-Bold", 19)
cv.drawCentredString(76, y0 - card_h + card_h / 2 - 4, num)
# Title + desc
draw_text_left(cv, title_, 114, y0 - card_h + card_h / 2 + 16,
"Helvetica-Bold", 24, NAVY)
multiline_left(cv, desc, 114, y0 - card_h + card_h / 2 - 8,
W - 80 - 114 - 20, "Helvetica", 19, SLATE, lh=24)
cv.showPage()
# ─────────────────────────────────────────────────────────────────────────────
# SLIDE 4 – BATTERY-OPERATED AID CARE
# ─────────────────────────────────────────────────────────────────────────────
cv.setFillColor(HexColor("#FFFAF6")); cv.rect(0, 0, W, H, fill=1, stroke=0)
draw_frame(cv, 4, header_bg=HexColor("#3D1800"))
# Section header
cv.setFillColor(ORANGE_PALE)
cv.roundRect(40, H - 268, W - 80, 118, 14, fill=1, stroke=0)
cv.setFillColor(ORANGE); cv.roundRect(40, H - 268, 6, 118, 3, fill=1, stroke=0)
pill_tag(cv, "🔋 BATTERY-OPERATED AIDS", cx(), H - 188, ORANGE, WHITE_PURE, 19)
draw_text_centered(cv, "Nightly Routine & Tips", H - 238, "Helvetica-Bold", 38, HexColor("#3D1800"))
steps = [
("1", "Open Battery Door Fully Each Night",
"Allows air circulation — prevents moisture trapped against contacts"),
("2", "Place in Drying Box for 6–8 Hours",
"Electronic dryer or desiccant jar — gold standard overnight routine"),
("3", "Store Spare Batteries Airtight",
"Sealed container + silica gel packet; never in a damp pocket or bag"),
("4", "Change Batteries Every 5–6 Days",
"Humidity shortens battery life; carry a spare pair at all times"),
("5", "Check Contacts for Corrosion",
"White or green residue → clean gently with a dry cotton swab"),
("6", "Remove Batteries for Long Storage",
"If unused for more than a day, remove batteries to prevent corrosion"),
]
card_h = 140; gap = 10; start_y = H - 302
for i, (num, title_, desc) in enumerate(steps):
y0 = start_y - i * (card_h + gap)
left_accent_card(cv, 40, y0 - card_h, W - 80, card_h, ORANGE,
fill=WHITE_PURE if i % 2 == 0 else ORANGE_PALE)
icon_badge(cv, 86, y0 - card_h / 2, 26, ORANGE, num, font_size=22)
draw_text_left(cv, title_, 130, y0 - card_h / 2 + 20, "Helvetica-Bold", 24,
HexColor("#3D1800"))
multiline_left(cv, desc, 130, y0 - card_h / 2 - 6,
W - 80 - 130 - 20, "Helvetica", 19, SLATE, lh=24)
cv.showPage()
# ─────────────────────────────────────────────────────────────────────────────
# SLIDE 5 – RECHARGEABLE AID CARE
# ─────────────────────────────────────────────────────────────────────────────
cv.setFillColor(HexColor("#F5FDF8")); cv.rect(0, 0, W, H, fill=1, stroke=0)
draw_frame(cv, 5, header_bg=HexColor("#0D2B1A"))
cv.setFillColor(GREEN_PALE)
cv.roundRect(40, H - 268, W - 80, 118, 14, fill=1, stroke=0)
cv.setFillColor(GREEN); cv.roundRect(40, H - 268, 6, 118, 3, fill=1, stroke=0)
pill_tag(cv, "⚡ RECHARGEABLE AIDS", cx(), H - 188, GREEN, WHITE_PURE, 19)
draw_text_centered(cv, "Nightly Routine & Tips", H - 238, "Helvetica-Bold", 38,
HexColor("#0D2B1A"))
# Important callout
cv.setFillColor(HexColor("#FFFBEA"))
cv.setStrokeColor(GOLD); cv.setLineWidth(1.5)
cv.roundRect(40, H - 340, W - 80, 54, 10, fill=1, stroke=1)
cv.setFillColor(GOLD); cv.setFont("Helvetica-Bold", 20)
cv.drawCentredString(cx(), H - 318,
"⚠ Cannot open battery compartment — nightly drying is CRITICAL")
steps_r = [
("1", "Power OFF Before Placing in Charger",
"Press and hold the power button until the device turns off"),
("2", "Wipe Charging Contacts Dry",
"Metal contacts on the base must be moisture-free before charging"),
("3", "Use Charging Case Overnight",
"Phonak, ReSound & Starkey cases often include a built-in dehumidifier"),
("4", "Separate Dryer if No Built-In",
"Use electronic dryer for 30–60 min before placing in the charger"),
("5", "Never Charge While Wet",
"Moisture on contacts causes short circuits and charging errors"),
("6", "No Hairdryer or Oven",
"Heat damages lithium-ion battery — voids warranty and risks fire"),
]
card_h = 130; gap = 10; start_y = H - 372
for i, (num, title_, desc) in enumerate(steps_r):
y0 = start_y - i * (card_h + gap)
left_accent_card(cv, 40, y0 - card_h, W - 80, card_h, GREEN,
fill=WHITE_PURE if i % 2 == 0 else GREEN_PALE)
icon_badge(cv, 86, y0 - card_h / 2, 26, GREEN, num, font_size=22)
draw_text_left(cv, title_, 130, y0 - card_h / 2 + 18, "Helvetica-Bold", 23,
HexColor("#0D2B1A"))
multiline_left(cv, desc, 130, y0 - card_h / 2 - 6,
W - 80 - 130 - 20, "Helvetica", 19, SLATE, lh=24)
cv.showPage()
# ─────────────────────────────────────────────────────────────────────────────
# SLIDE 6 – DRYING TOOLS
# ─────────────────────────────────────────────────────────────────────────────
cv.setFillColor(OFF_WHITE); cv.rect(0, 0, W, H, fill=1, stroke=0)
draw_frame(cv, 6)
cv.setFillColor(TEAL_PALE)
cv.roundRect(40, H - 260, W - 80, 118, 14, fill=1, stroke=0)
cv.setFillColor(TEAL); cv.roundRect(40, H - 260, 6, 118, 3, fill=1, stroke=0)
draw_text_centered(cv, "Drying Tools Guide", H - 182, "Helvetica-Bold", 46, NAVY)
draw_text_centered(cv, "Choose the right dryer for your aid", H - 230,
"Helvetica", 25, SLATE)
tools = [
(SLATE, "🫙", "Silica Gel Desiccant Jar",
"Passive moisture absorption. Budget-friendly option.",
"Replace silica gel monthly.", "Both aids"),
(TEAL, "🔌", "Electronic Drying Box",
"Gentle heat + airflow. Most effective daily dryer.",
"Use for 6–8 hours overnight — gold standard.", "Both aids"),
(GOLD, "☀", "UV-C Drying Box",
"UV light kills bacteria & fungi + desiccant drying.",
"Best choice during monsoon due to infection risk.", "Both aids"),
(GREEN, "⚡", "Charging Case with Dryer",
"Dries the hearing aid while it charges overnight.",
"Check your model — not all charging cases include this.", "Rechargeable only"),
]
card_h = 192; gap = 14; start_y = H - 298
for i, (col, emoji, name, line1, line2, for_) in enumerate(tools):
y0 = start_y - i * (card_h + gap)
left_accent_card(cv, 40, y0 - card_h, W - 80, card_h, col,
fill=TEAL_PALE if i % 2 == 0 else WHITE_PURE)
# Emoji circle
cv.setFillColor(HexColor(col.hexval() + "20"))
cv.circle(96, y0 - card_h / 2 + 6, 36, fill=1, stroke=0)
cv.setFillColor(col); cv.setFont("Helvetica", 36)
cv.drawCentredString(96, y0 - card_h / 2 - 5, emoji)
# Name
draw_text_left(cv, name, 150, y0 - card_h / 2 + 38, "Helvetica-Bold", 24, NAVY)
draw_text_left(cv, line1, 150, y0 - card_h / 2 + 12, "Helvetica", 19, SLATE)
draw_text_left(cv, line2, 150, y0 - card_h / 2 - 12, "Helvetica", 19, SLATE)
# For badge
badge_col = TEAL if for_ == "Both aids" else GREEN
bw = cv.stringWidth(for_, "Helvetica-Bold", 16) + 18
cv.setFillColor(badge_col)
cv.roundRect(W - 60 - bw, y0 - card_h / 2 - 34, bw, 28, 6, fill=1, stroke=0)
cv.setFillColor(WHITE_PURE); cv.setFont("Helvetica-Bold", 16)
cv.drawRightString(W - 52, y0 - card_h / 2 - 20, for_)
cv.showPage()
# ─────────────────────────────────────────────────────────────────────────────
# SLIDE 7 – EMERGENCY: AID GOT WET
# ─────────────────────────────────────────────────────────────────────────────
cv.setFillColor(HexColor("#FDF5F5")); cv.rect(0, 0, W, H, fill=1, stroke=0)
draw_frame(cv, 7, header_bg=HexColor("#4A0000"))
cv.setFillColor(RED_PALE)
cv.roundRect(40, H - 268, W - 80, 118, 14, fill=1, stroke=0)
cv.setFillColor(RED); cv.roundRect(40, H - 268, 6, 118, 3, fill=1, stroke=0)
pill_tag(cv, "🚨 EMERGENCY PROTOCOL", cx(), H - 192, RED, WHITE_PURE, 20)
draw_text_centered(cv, "Hearing Aid Got Wet?", H - 238, "Helvetica-Bold", 40,
HexColor("#4A0000"))
steps_e = [
("Remove from water/rain IMMEDIATELY", "Do not delay even a few seconds"),
("Power OFF — hold power button", "Prevents short circuit from active current"),
("Do NOT press other buttons", "Pushing buttons drives moisture deeper inside"),
("Wipe exterior with dry lint-free cloth", "Gently dab all ports, contacts & seams"),
("Battery aid: remove battery, open door", "Exposes the interior to air drying"),
("Place in drying box for 24–48 hours", "Electronic dryer or desiccant jar"),
("Rechargeable: do NOT charge until fully dry", "Moisture on contacts = short circuit"),
("Do NOT use rice, hairdryer or oven", "Rice clogs ports; heat destroys components"),
("Still fails at 48 hrs? Call audiologist", "Internal moisture needs professional drying"),
]
card_h = 114; gap = 8; start_y = H - 304
for i, (step, note) in enumerate(steps_e):
y0 = start_y - i * (card_h + gap)
fill = RED_PALE if i % 2 == 0 else WHITE_PURE
left_accent_card(cv, 40, y0 - card_h, W - 80, card_h, RED, fill=fill)
icon_badge(cv, 82, y0 - card_h / 2, 22, RED, str(i + 1), font_size=19)
draw_text_left(cv, step, 122, y0 - card_h / 2 + 16, "Helvetica-Bold", 22,
HexColor("#4A0000"))
draw_text_left(cv, note, 122, y0 - card_h / 2 - 8, "Helvetica", 18, SLATE)
cv.showPage()
# ─────────────────────────────────────────────────────────────────────────────
# SLIDE 8 – DAILY CHECKLIST
# ─────────────────────────────────────────────────────────────────────────────
cv.setFillColor(OFF_WHITE); cv.rect(0, 0, W, H, fill=1, stroke=0)
draw_frame(cv, 8)
cv.setFillColor(TEAL_PALE)
cv.roundRect(40, H - 260, W - 80, 118, 14, fill=1, stroke=0)
cv.setFillColor(TEAL); cv.roundRect(40, H - 260, 6, 118, 3, fill=1, stroke=0)
draw_text_centered(cv, "Daily Monsoon Checklist", H - 180, "Helvetica-Bold", 44, NAVY)
draw_text_centered(cv, "Screenshot & tick these off each evening", H - 228,
"Helvetica-Oblique", 23, TEAL_MED)
checks = [
("Wipe aids with a dry lint-free cloth", "Both"),
("Brush microphone & sound ports", "Both"),
("Open battery door fully for the night", "Battery"),
("Wipe charging contacts before placing in charger", "Rechargeable"),
("Place aids in drying box / charging case", "Both"),
("Checked battery & replaced if weak", "Battery"),
("Spare batteries in airtight + silica gel container","Battery"),
("Wore protective sleeve when outdoors", "Both"),
("Did NOT store aids in the bathroom", "Both"),
("Applied hairspray/sunscreen before inserting aids", "Both"),
]
row_h = 104; gap = 8; start_y = H - 298
badge_cols = {"Both": TEAL, "Battery": ORANGE, "Rechargeable": GREEN}
for i, (task, type_) in enumerate(checks):
y0 = start_y - i * (row_h + gap)
bg = TEAL_PALE if i % 2 == 0 else WHITE_PURE
card(cv, 40, y0 - row_h, W - 80, row_h, fill=bg, stroke_col=RULE, radius=10)
# Checkbox
cv.setStrokeColor(TEAL_MED); cv.setLineWidth(1.5)
cv.setFillColor(WHITE_PURE)
cv.roundRect(58, y0 - row_h + row_h / 2 - 16, 32, 32, 5, fill=1, stroke=1)
# Tick mark inside
cv.setStrokeColor(RULE); cv.setLineWidth(1)
cv.line(64, y0 - row_h + row_h / 2 - 3, 68, y0 - row_h + row_h / 2 - 8)
cv.line(68, y0 - row_h + row_h / 2 - 8, 82, y0 - row_h + row_h / 2 + 6)
# Task
draw_text_left(cv, task, 106, y0 - row_h + row_h / 2 - 8,
"Helvetica", 21, NAVY)
# Type badge
bc = badge_cols.get(type_, TEAL)
bw = cv.stringWidth(type_, "Helvetica-Bold", 15) + 14
cv.setFillColor(bc)
cv.roundRect(W - 52 - bw, y0 - row_h + row_h / 2 - 14, bw, 28, 6, fill=1, stroke=0)
cv.setFillColor(WHITE_PURE); cv.setFont("Helvetica-Bold", 15)
cv.drawRightString(W - 44, y0 - row_h + row_h / 2 - 2, type_)
cv.showPage()
# ─────────────────────────────────────────────────────────────────────────────
# SLIDE 9 – CLOSING / BRAND CTA
# ─────────────────────────────────────────────────────────────────────────────
# Full navy background
cv.setFillColor(NAVY); cv.rect(0, 0, W, H, fill=1, stroke=0)
# Teal accent strip at very top
cv.setFillColor(TEAL_MED); cv.rect(0, H - 8, W, 8, fill=1, stroke=0)
# Large decorative circles
cv.setFillColor(HexColor("#FFFFFF06"))
cv.circle(W * 0.15, H * 0.82, 200, fill=1, stroke=0)
cv.circle(W * 0.88, H * 0.18, 160, fill=1, stroke=0)
cv.circle(cx(), H * 0.5, 340, fill=1, stroke=0)
# Clinic name block at top
cv.setFillColor(HexColor("#0E3A54"))
cv.roundRect(60, H - 160, W - 120, 80, 14, fill=1, stroke=0)
cv.setFillColor(TEAL_MED); cv.setFont("Helvetica-Bold", 24)
cv.drawCentredString(cx(), H - 130, CLINIC_NAME)
# Big checkmark badge
cv.setFillColor(TEAL_MED)
cv.circle(cx(), H * 0.60, 110, fill=1, stroke=0)
cv.setFillColor(WHITE_PURE); cv.setFont("Helvetica-Bold", 90)
cv.drawCentredString(cx(), H * 0.60 - 32, "✓")
draw_text_centered(cv, "You're All Set!", H * 0.46, "Helvetica-Bold", 62, WHITE_PURE)
draw_text_centered(cv, "Keep your hearing aids safe", H * 0.46 - 72,
"Helvetica", 28, SKY)
draw_text_centered(cv, "this monsoon season.", H * 0.46 - 110, "Helvetica", 28, SKY)
horiz_rule(cv, H * 0.37, color=HexColor("#FFFFFF20"), lw=1)
# Takeaway bullets
bullets = [
"5 minutes of nightly care = months of extra device life",
"Nightly drying box is your best investment this season",
"Share this guide with every family caregiver",
"Book your mid-monsoon audiologist check-up today",
]
by = H * 0.34
for b in bullets:
cv.setFont("Helvetica", 23); cv.setFillColor(SKY)
cv.drawCentredString(cx(), by, "— " + b + " —")
by -= 46
# CTA button
cv.setFillColor(TEAL_MED)
cv.roundRect(cx() - 260, H * 0.15, 520, 72, 36, fill=1, stroke=0)
cv.setFillColor(WHITE_PURE); cv.setFont("Helvetica-Bold", 26)
cv.drawCentredString(cx(), H * 0.15 + 26, "Save · Share · Follow for more tips")
# Footer note
cv.setFillColor(MID_GREY); cv.setFont("Helvetica", 18)
cv.drawCentredString(cx(), 30, "Consult your audiologist for personalised hearing care advice")
dot_row(cv, 9, 8)
cv.showPage()
# ── Save ──────────────────────────────────────────────────────────────────────
cv.save()
print(f"Saved: {out}")
cd /tmp/workspace/hearing-aid-guide && python create_branded_reel.py 2>&1
ls -lh /tmp/workspace/hearing-aid-guide/Hearing_Aid_Monsoon_Branded_Reel.pdf
Hearing Aid Monsoon Branded Reel
PDF Document · PDF
| Slide | Content | Design Detail |
|---|---|---|
| 1 - Cover | Title + battery/rechargeable pills | Navy header, teal central icon badge, swipe CTA |
| 2 - Risk Factors | 5 threat cards | Colour-coded left accent bars (red/orange/gold/green/teal) |
| 3 - General Rules | 8 rules with numbered badges | Alternating teal-pale/white rows |
| 4 - Battery Care | 6 nightly steps | Warm orange theme, orange numbered circles |
| 5 - Rechargeable Care | 6 nightly steps + critical callout banner | Clinical green theme, gold warning strip |
| 6 - Drying Tools | 4 dryer types with "Both/Rechargeable" badges | White card layout, teal accents |
| 7 - Emergency | 9-step protocol | Red accent bars, red numbered badges |
| 8 - Daily Checklist | 10 tick-box rows with type badges | Orange/Green/Teal type-coding |
| 9 - Closing CTA | Full navy, large ✓, takeaways + Save/Share button | Full branded dark close |
[Your Clinic Name] in the top-left header bar - just replace with your actual nameCreate a short video script to narrate each reel slide
"Monsoon season is here — and for hearing aid users, it's the most important time of year to take care of your device. Whether you use battery-operated or rechargeable hearing aids, this guide is for you. Swipe through — it could save your hearing aid."
"Here's why monsoon is risky for your hearing aids. Moisture corrodes the internal circuits. Water blocks the microphone and receiver ports. High humidity drains your battery up to 50% faster. And persistent dampness can actually grow mould inside your ear mould. These are real, preventable problems — so let's fix them."
"First, the rules that apply to every hearing aid. Wipe your aids every evening with a dry cloth. Brush the microphone ports daily. Never — and I mean never — store your aids in the bathroom. Use a protective sleeve when stepping outside. And always wear a hat or carry an umbrella in the rain. These five habits alone will protect your device all season."
"If you use battery-operated aids, this slide is for you. Every single night, open the battery door completely — this allows air to circulate and stops moisture from building up on the contacts. Place your aids in a drying box for six to eight hours. Keep your spare batteries in an airtight container with a silica gel packet. And during monsoon, expect to change batteries every five to six days instead of the usual seven to ten."
"Rechargeable users — listen carefully. You cannot open the battery compartment, which means your nightly drying routine is absolutely critical. Always power the device off before placing it in the charger. Wipe the charging contacts dry — every night. Many modern charging cases from Phonak, ReSound, and Starkey have a built-in dehumidifier — use it. And never, ever charge a wet hearing aid — it can short circuit."
"So which drying tool should you use? A silica gel jar is a great budget option — just replace the silica monthly. An electronic drying box with gentle heat and airflow is the gold standard — six to eight hours overnight. A UV-C drying box also kills bacteria and fungi, which is especially valuable during monsoon. And if your charging case has a built-in dryer — that's your best option if you're on rechargeables."
"If your hearing aid gets wet — act fast. Remove it from the water immediately. Power it off. Do not press any other buttons. Wipe the outside dry gently. For battery aids, remove the battery and open the door. Then place it in a drying box for twenty-four to forty-eight hours. Rechargeable users — do not put it back in the charger until it's completely dry. And please — no rice, no hairdryer, no oven. If it still doesn't work after 48 hours, call your audiologist."
"Here's your simple daily checklist. Screenshot this. Wipe your aids. Brush the ports. Open the battery door or wipe the charging contacts. Put them in the drying box or charging case. Check the battery level. Keep spares airtight. Wear a protective sleeve outdoors. Five minutes. Every night. That's all it takes."
"That's your complete monsoon hearing aid care guide. Five minutes of care each night can add months — even years — to the life of your device. Save this reel, share it with a family member who wears hearing aids, and follow us for more tips from our clinic. And if you have any concerns this monsoon — our door is always open. Your hearing matters."
| Element | Recommendation |
|---|---|
| Speaker | Audiologist on camera OR voiceover only |
| Background music | Soft, calm — Lo-fi or light clinical background track |
| Caption style | Auto-captions ON (accessibility + silent viewers) |
| Slide timing | 3–5 sec per slide; total reel ~45–60 sec at normal swipe pace |
| Hook text overlay | Slide 1: "Is monsoon destroying your hearing aid? 👇" |
| CTA text overlay | Slide 9: "Follow + Save for more hearing health tips" |
| Language option | Script can be recorded in Hindi/regional language over the same slides |
Create a downloadable PDF script version for team use
~/hearing-aid-guide/create_script_pdf.py
"""
Branded Clinical Script PDF – Hearing Aid Monsoon Care Reel
Professional team-use format: script + production notes per slide
"""
from reportlab.platypus import (
SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
HRFlowable, KeepTogether, PageBreak
)
from reportlab.lib.pagesizes import A4
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.units import cm
from reportlab.lib.enums import TA_LEFT, TA_CENTER, TA_JUSTIFY
from reportlab.lib.colors import HexColor, white, black
from reportlab.platypus.flowables import Flowable
# ── Colours ───────────────────────────────────────────────────────────────────
NAVY = HexColor("#0A2540")
TEAL = HexColor("#0B7285")
TEAL_MED = HexColor("#1A9EBA")
TEAL_PALE = HexColor("#E8F7FA")
SLATE = HexColor("#3D5A6C")
OFF_WHITE = HexColor("#F7FBFD")
ORANGE = HexColor("#D95F00")
ORANGE_PALE = HexColor("#FFF0E6")
GREEN = HexColor("#1A7A4A")
GREEN_PALE = HexColor("#E8F5EE")
RED = HexColor("#B02020")
RED_PALE = HexColor("#FDF0F0")
GOLD = HexColor("#C89B14")
GOLD_PALE = HexColor("#FFFBEA")
MID_GREY = HexColor("#8AA0AE")
RULE = HexColor("#D0E8EF")
LIGHT_GREY = HexColor("#F0F4F7")
DARK_GREY = HexColor("#333333")
W, H = A4
# ── Page callbacks ─────────────────────────────────────────────────────────────
def on_first_page(canvas, doc):
_draw_header(canvas, is_first=True)
_draw_footer(canvas, doc.page)
def on_later_pages(canvas, doc):
_draw_header(canvas, is_first=False)
_draw_footer(canvas, doc.page)
def _draw_header(canvas, is_first=False):
if is_first:
# Full navy banner
canvas.setFillColor(NAVY)
canvas.rect(0, H - 2.6*cm, W, 2.6*cm, fill=1, stroke=0)
canvas.setFillColor(TEAL_MED)
canvas.rect(0, H - 2.6*cm - 5, W, 5, fill=1, stroke=0)
canvas.setFillColor(white)
canvas.setFont("Helvetica-Bold", 13)
canvas.drawString(1.8*cm, H - 1.5*cm, "[Your Clinic Name]")
canvas.setFont("Helvetica", 10)
canvas.setFillColor(HexColor("#AACEDD"))
canvas.drawRightString(W - 1.8*cm, H - 1.5*cm, "INTERNAL USE ONLY · TEAM SCRIPT DOCUMENT")
else:
canvas.setFillColor(NAVY)
canvas.rect(0, H - 1.4*cm, W, 1.4*cm, fill=1, stroke=0)
canvas.setFillColor(TEAL_MED)
canvas.rect(0, H - 1.4*cm - 4, W, 4, fill=1, stroke=0)
canvas.setFillColor(white)
canvas.setFont("Helvetica-Bold", 9)
canvas.drawString(1.8*cm, H - 0.96*cm,
"Hearing Aid Monsoon Care — Video Narration Script")
canvas.setFont("Helvetica", 9)
canvas.setFillColor(HexColor("#AACEDD"))
canvas.drawRightString(W - 1.8*cm, H - 0.96*cm,
f"[Your Clinic Name] · TEAM USE")
def _draw_footer(canvas, page_num):
canvas.setFillColor(NAVY)
canvas.rect(0, 0, W, 1.1*cm, fill=1, stroke=0)
canvas.setFillColor(TEAL_MED)
canvas.rect(0, 1.1*cm, W, 3, fill=1, stroke=0)
canvas.setFillColor(HexColor("#7FAABD"))
canvas.setFont("Helvetica", 8)
canvas.drawString(1.8*cm, 0.38*cm,
"Confidential · For internal team and clinical staff use only · July 2026")
canvas.drawRightString(W - 1.8*cm, 0.38*cm, f"Page {page_num}")
# ── Custom flowable: slide header band ────────────────────────────────────────
class SlideHeader(Flowable):
def __init__(self, slide_num, title, duration, accent_col=TEAL, width=None):
super().__init__()
self.num = slide_num
self.title = title
self.duration = duration
self.accent = accent_col
self._w = width
self.height = 52
def wrap(self, aW, aH):
self._bw = self._w or aW
return self._bw, self.height + 4
def draw(self):
c = self.canv
bw, bh = self._bw, self.height
# Background
c.setFillColor(self.accent)
c.roundRect(0, 0, bw, bh, 8, fill=1, stroke=0)
# Slide number badge
c.setFillColor(HexColor("#FFFFFF30"))
c.circle(bh / 2, bh / 2, bh / 2 - 4, fill=1, stroke=0)
c.setFillColor(white)
c.setFont("Helvetica-Bold", 18)
c.drawCentredString(bh / 2, bh / 2 - 7, str(self.num))
# Title
c.setFillColor(white)
c.setFont("Helvetica-Bold", 13)
c.drawString(bh + 10, bh / 2 + 3, self.title)
# Duration pill (right side)
dur_text = f"⏱ {self.duration}"
c.setFont("Helvetica", 10)
dw = c.stringWidth(dur_text, "Helvetica", 10) + 16
c.setFillColor(HexColor("#FFFFFF25"))
c.roundRect(bw - dw - 8, bh / 2 - 11, dw, 22, 5, fill=1, stroke=0)
c.setFillColor(white)
c.drawString(bw - dw - 0, bh / 2 - 5, dur_text)
# ── Styles ─────────────────────────────────────────────────────────────────────
def make_styles():
base = getSampleStyleSheet()
doc_title = ParagraphStyle("DocTitle",
fontName="Helvetica-Bold", fontSize=26, textColor=white,
leading=32, alignment=TA_CENTER, spaceAfter=4)
doc_subtitle = ParagraphStyle("DocSubtitle",
fontName="Helvetica", fontSize=13, textColor=HexColor("#B2E0EC"),
leading=18, alignment=TA_CENTER, spaceAfter=2)
doc_meta = ParagraphStyle("DocMeta",
fontName="Helvetica-Oblique", fontSize=10, textColor=HexColor("#7FAABD"),
alignment=TA_CENTER, spaceAfter=0)
section_label = ParagraphStyle("SectionLabel",
fontName="Helvetica-Bold", fontSize=9, textColor=TEAL_MED,
leading=12, spaceBefore=14, spaceAfter=2,
textTransform="uppercase")
script_text = ParagraphStyle("ScriptText",
fontName="Helvetica-Oblique", fontSize=11, textColor=DARK_GREY,
leading=17, spaceAfter=6, alignment=TA_JUSTIFY,
leftIndent=10, rightIndent=10)
body = ParagraphStyle("Body",
fontName="Helvetica", fontSize=10, textColor=SLATE,
leading=15, spaceAfter=4, alignment=TA_JUSTIFY)
bold_body = ParagraphStyle("BoldBody",
fontName="Helvetica-Bold", fontSize=10, textColor=NAVY,
leading=14, spaceAfter=3)
note_text = ParagraphStyle("NoteText",
fontName="Helvetica", fontSize=9.5, textColor=SLATE,
leading=14, spaceAfter=2)
note_bold = ParagraphStyle("NoteBold",
fontName="Helvetica-Bold", fontSize=9.5, textColor=NAVY,
leading=14, spaceAfter=2)
tip = ParagraphStyle("Tip",
fontName="Helvetica", fontSize=9.5, textColor=HexColor("#1B5E20"),
backColor=GREEN_PALE, leading=14,
leftIndent=6, rightIndent=6, spaceBefore=2, spaceAfter=4,
borderPad=5)
warn = ParagraphStyle("Warn",
fontName="Helvetica-Bold", fontSize=9.5, textColor=RED,
backColor=RED_PALE, leading=14,
leftIndent=6, rightIndent=6, spaceBefore=2, spaceAfter=4,
borderPad=5)
return dict(
doc_title=doc_title, doc_subtitle=doc_subtitle, doc_meta=doc_meta,
section_label=section_label, script_text=script_text,
body=body, bold_body=bold_body,
note_text=note_text, note_bold=note_bold,
tip=tip, warn=warn,
)
# ── Build ──────────────────────────────────────────────────────────────────────
out = "/tmp/workspace/hearing-aid-guide/Hearing_Aid_Monsoon_Video_Script.pdf"
doc = SimpleDocTemplate(
out, pagesize=A4,
leftMargin=1.8*cm, rightMargin=1.8*cm,
topMargin=3.2*cm, bottomMargin=1.8*cm,
title="Hearing Aid Monsoon Care — Video Narration Script",
author="[Your Clinic Name]",
)
S = make_styles()
story = []
# ── COVER BLOCK ───────────────────────────────────────────────────────────────
# Navy cover card
cover_inner = [
[Paragraph("Hearing Aid Monsoon Care", S["doc_title"])],
[Paragraph("Video Narration Script", S["doc_title"])],
[Spacer(1, 6)],
[Paragraph("Slide-by-Slide Script · Production Notes · Team Reference",
S["doc_subtitle"])],
[Spacer(1, 4)],
[Paragraph("[Your Clinic Name] · July 2026 · Internal Team Document",
S["doc_meta"])],
]
cover_table = Table(cover_inner,
colWidths=[W - 3.6*cm],
style=TableStyle([
("BACKGROUND", (0,0), (-1,-1), NAVY),
("TOPPADDING", (0,0), (-1,-1), 10),
("BOTTOMPADDING", (0,0), (-1,-1), 10),
("LEFTPADDING", (0,0), (-1,-1), 20),
("RIGHTPADDING", (0,0), (-1,-1), 20),
("ROUNDEDCORNERS", (0,0), (-1,-1), [12,12,12,12]),
]))
story.append(cover_table)
story.append(Spacer(1, 0.4*cm))
# Quick-reference metadata table
meta_rows = [
["Format", "Instagram / Facebook / YouTube Shorts Carousel Reel"],
["Total Slides", "9 slides"],
["Total Narration Time", "~90–100 seconds (full read-through)"],
["Recommended Slide Duration", "3–5 seconds per slide"],
["Tone", "Warm · Professional · Clinical · Patient-facing"],
["Speaker", "Audiologist on camera OR professional voiceover"],
["Background Music", "Soft lo-fi or light clinical background track"],
["Captions", "Auto-captions ON recommended (accessibility + silent viewers)"],
["Language Options", "Script may be translated and re-recorded in regional languages"],
]
meta_style = TableStyle([
("BACKGROUND", (0,0), (0,-1), TEAL_PALE),
("FONTNAME", (0,0), (0,-1), "Helvetica-Bold"),
("FONTNAME", (1,0), (1,-1), "Helvetica"),
("FONTSIZE", (0,0), (-1,-1), 9.5),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING", (0,0), (-1,-1), 5),
("LEFTPADDING", (0,0), (-1,-1), 8),
("GRID", (0,0), (-1,-1), 0.4, RULE),
("TEXTCOLOR", (0,0), (0,-1), NAVY),
("TEXTCOLOR", (1,0), (1,-1), SLATE),
("ROWBACKGROUNDS",(0,0), (-1,-1), [white, OFF_WHITE]),
("VALIGN", (0,0), (-1,-1), "MIDDLE"),
])
story.append(Table(meta_rows,
colWidths=[(W-3.6*cm)*0.3, (W-3.6*cm)*0.7],
style=meta_style))
story.append(Spacer(1, 0.3*cm))
# How to use box
how_data = [[Paragraph(
"<b>How to use this script:</b> Each section below corresponds to one carousel slide. "
"The NARRATION block is the exact script to read or record. The PRODUCTION NOTES "
"block contains on-screen text suggestions, music cues, and visual direction. "
"Highlighted boxes in <font color='#1A7A4A'>green</font> are speaker tips; "
"boxes in <font color='#B02020'>red</font> are critical warnings to emphasise in delivery.",
S["body"])]]
story.append(Table(how_data, colWidths=[W-3.6*cm],
style=TableStyle([
("BACKGROUND", (0,0), (-1,-1), GOLD_PALE),
("BOX", (0,0), (-1,-1), 1, GOLD),
("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(Spacer(1, 0.5*cm))
story.append(HRFlowable(width="100%", thickness=1, color=RULE))
story.append(Spacer(1, 0.3*cm))
# ── SLIDE BLOCK BUILDER ───────────────────────────────────────────────────────
def slide_block(num, title, duration, accent,
narration, prod_notes, tip_text=None, warn_text=None):
"""Returns a KeepTogether list of flowables for one slide."""
items = []
items.append(SlideHeader(num, title, duration, accent_col=accent))
items.append(Spacer(1, 0.2*cm))
# Narration
items.append(Paragraph("NARRATION", S["section_label"]))
# Script in a shaded card
script_rows = [[Paragraph(f'"{narration}"', S["script_text"])]]
items.append(Table(script_rows, colWidths=[W-3.6*cm],
style=TableStyle([
("BACKGROUND", (0,0), (-1,-1), TEAL_PALE),
("BOX", (0,0), (-1,-1), 1, RULE),
("TOPPADDING", (0,0), (-1,-1), 10),
("BOTTOMPADDING", (0,0), (-1,-1), 10),
("LEFTPADDING", (0,0), (-1,-1), 12),
("RIGHTPADDING", (0,0), (-1,-1), 12),
])))
items.append(Spacer(1, 0.15*cm))
# Production notes
items.append(Paragraph("PRODUCTION NOTES", S["section_label"]))
note_data = [[Paragraph(note, S["note_text"])] for note in prod_notes]
for row in note_data:
items.append(Table([row], colWidths=[W-3.6*cm],
style=TableStyle([
("TOPPADDING", (0,0), (-1,-1), 3),
("BOTTOMPADDING", (0,0), (-1,-1), 3),
("LEFTPADDING", (0,0), (-1,-1), 4),
])))
if tip_text:
items.append(Paragraph(f"💡 {tip_text}", S["tip"]))
if warn_text:
items.append(Paragraph(f"⚠ {warn_text}", S["warn"]))
items.append(Spacer(1, 0.35*cm))
items.append(HRFlowable(width="100%", thickness=0.5, color=RULE))
items.append(Spacer(1, 0.3*cm))
return KeepTogether(items)
# ── SLIDES ─────────────────────────────────────────────────────────────────────
story.append(slide_block(
num=1, title="COVER SLIDE", duration="~7 sec", accent=TEAL,
narration=(
"Monsoon season is here — and for hearing aid users, it's the most important time of year "
"to take care of your device. Whether you use battery-operated or rechargeable hearing aids, "
"this guide is for you. Swipe through — it could save your hearing aid."
),
prod_notes=[
"🎵 Music: Soft, upbeat intro track fades in as narration begins.",
"📱 On-screen hook text overlay: \"Is monsoon destroying your hearing aid? 👇\"",
"🎥 Visual: Clinic logo / name animates in; audiologist smiling to camera optional.",
"🎨 Slide shows: Title + battery/rechargeable type pills.",
"⏩ Transition: Smooth slide-in from left to next card.",
],
tip_text="Deliver the opening line with energy — the hook must stop the scroll."
))
story.append(slide_block(
num=2, title="WHY MONSOON IS DANGEROUS", duration="~12 sec", accent=RED,
narration=(
"Here's why monsoon is risky for your hearing aids. Moisture corrodes the internal circuits. "
"Water blocks the microphone and receiver ports. High humidity drains your battery up to "
"50% faster. And persistent dampness can actually grow mould inside your ear mould. "
"These are real, preventable problems — so let's fix them."
),
prod_notes=[
"🎵 Music: Settles to soft, calm background throughout the rest of the reel.",
"📱 Emphasise the word 'preventable' — reassures the viewer.",
"🎨 Slide shows: 5 colour-coded risk cards (corrosion, blocked ports, battery, mould, condensation).",
"🎥 Optional B-roll: Close-up of hearing aid in rain, condensation on glass.",
"⏩ Transition: Each risk card can 'tick' in sequentially if using CapCut/video.",
],
warn_text="Do not rush this slide. Let each risk land before moving on."
))
story.append(slide_block(
num=3, title="GENERAL RULES — BOTH AID TYPES", duration="~14 sec", accent=TEAL,
narration=(
"First, the rules that apply to every hearing aid. Wipe your aids every evening with a dry cloth. "
"Brush the microphone ports daily. Never — and I mean never — store your aids in the bathroom. "
"Use a protective sleeve when stepping outside. And always wear a hat or carry an umbrella in the rain. "
"These five habits alone will protect your device all season."
),
prod_notes=[
"📱 On-screen text: Highlight each rule as it is spoken (sequential text animation).",
"🎨 Slide shows: 8 alternating-row rule tiles with numbered badges.",
"🎥 Optional: Speaker holds up fingers to count the 5 habits — very engaging on-camera.",
"⏱ Pause slightly after 'never store your aids in the bathroom' — let it land.",
"🗣 Tone: Friendly but firm. Like a trusted clinician giving a quick debrief.",
],
tip_text="The 'bathroom' rule surprises most people — give it a beat of emphasis."
))
story.append(slide_block(
num=4, title="BATTERY-OPERATED AID CARE", duration="~16 sec", accent=ORANGE,
narration=(
"If you use battery-operated aids, this slide is for you. Every single night, open the battery "
"door completely — this allows air to circulate and stops moisture from building up on the contacts. "
"Place your aids in a drying box for six to eight hours. Keep your spare batteries in an airtight "
"container with a silica gel packet. And during monsoon, expect to change batteries every five to "
"six days instead of the usual seven to ten."
),
prod_notes=[
"📱 On-screen graphic: Animated arrow opening battery door is very effective here.",
"🎨 Slide shows: Orange-themed 6-step card layout.",
"🎥 B-roll suggestion: Hands demonstrating opening battery door and placing in drying jar.",
"⏱ Slowdown at '6–8 hours' and '5–6 days' — these are concrete numbers viewers need to remember.",
"🗣 Tone: Practical, instructional — like showing a patient during a fitting session.",
],
tip_text="Use a physical drying box as a prop on camera if recording in-clinic."
))
story.append(slide_block(
num=5, title="RECHARGEABLE AID CARE", duration="~16 sec", accent=GREEN,
narration=(
"Rechargeable users — listen carefully. You cannot open the battery compartment, which means "
"your nightly drying routine is absolutely critical. Always power the device off before placing "
"it in the charger. Wipe the charging contacts dry — every night. Many modern charging cases "
"from Phonak, ReSound, and Starkey have a built-in dehumidifier — use it. And never, ever "
"charge a wet hearing aid — it can short circuit."
),
prod_notes=[
"📱 On-screen callout box: 'Cannot open battery compartment!' — matches the slide design.",
"🎨 Slide shows: Green-themed steps + gold warning banner.",
"🎥 B-roll suggestion: Demonstrating wiping charging contacts, placing in charging case.",
"⏱ Emphasise 'never, ever charge a wet hearing aid' — pause before and after.",
"🗣 Tone: Slightly more urgent for the 'cannot open' callout, then reassuring for the steps.",
"📌 Mention specific brands (Phonak, ReSound, Starkey) to build clinical credibility.",
],
warn_text="'Never charge a wet hearing aid' must be the most emphatic line in this slide."
))
story.append(slide_block(
num=6, title="DRYING TOOLS GUIDE", duration="~14 sec", accent=TEAL_MED,
narration=(
"So which drying tool should you use? A silica gel jar is a great budget option — just "
"replace the silica monthly. An electronic drying box with gentle heat and airflow is the "
"gold standard — six to eight hours overnight. A UV-C drying box also kills bacteria and "
"fungi, which is especially valuable during monsoon. And if your charging case has a built-in "
"dryer — that's your best option if you're on rechargeables."
),
prod_notes=[
"📱 On-screen text: Each tool name highlights as it is mentioned.",
"🎨 Slide shows: 4 white cards with left-accent colour bars and 'Both / Rechargeable' badges.",
"🎥 B-roll: Show physical products — silica jar, Phonak drying case, UV-C box.",
"🗣 Tone: Informative, comparative — help the viewer make a choice.",
"🛒 Optional CTA text overlay: 'Ask your audiologist which dryer suits your model'.",
],
tip_text="If your clinic sells drying products, this slide is a natural, non-pushy product mention."
))
story.append(slide_block(
num=7, title="EMERGENCY — AID GOT WET", duration="~18 sec", accent=RED,
narration=(
"If your hearing aid gets wet — act fast. Remove it from the water immediately. Power it off. "
"Do not press any other buttons. Wipe the outside dry gently. For battery aids, remove the "
"battery and open the door. Then place it in a drying box for twenty-four to forty-eight hours. "
"Rechargeable users — do not put it back in the charger until it's completely dry. And please — "
"no rice, no hairdryer, no oven. If it still doesn't work after 48 hours, call your audiologist."
),
prod_notes=[
"📱 Text overlay: Step numbers animate in one by one — pacing matches the narration.",
"🎨 Slide shows: Red-accented 9-step emergency list.",
"🎵 Music: Very slight tension cue, then returns to reassuring tone at 'call your audiologist'.",
"🎥 On-camera: Urgent but calm expression — clinic is a trusted resource in a crisis.",
"⏱ This is the longest slide — allow full 18–20 seconds; do not rush.",
"🗣 'No rice, no hairdryer, no oven' — say this with a slight pause between each; it's memorable.",
],
warn_text=(
"The rice myth is very common — call it out clearly. Many patients damage aids further by using rice."
)
))
story.append(slide_block(
num=8, title="DAILY CHECKLIST", duration="~12 sec", accent=TEAL,
narration=(
"Here's your simple daily checklist. Screenshot this. Wipe your aids. Brush the ports. "
"Open the battery door or wipe the charging contacts. Put them in the drying box or charging case. "
"Check the battery level. Keep spares airtight. Wear a protective sleeve outdoors. "
"Five minutes. Every night. That's all it takes."
),
prod_notes=[
"📱 Text overlay at start: 'SCREENSHOT THIS ↓' — drives save action.",
"🎨 Slide shows: 10-item tick-box checklist with orange/green/teal type badges.",
"🎵 Music: Pace picks up slightly to match the checklist rhythm.",
"🎥 Consider pointing at phone or a printed checklist on camera for visual engagement.",
"🗣 Tone: Brisk, confident, rhythmic — like reading a pre-flight checklist.",
"⏱ Slow down on the final three words: 'Five. Minutes. Every night.' — maximum impact.",
],
tip_text="'Screenshot this' is one of the highest-save CTAs on educational reels — always include it."
))
story.append(slide_block(
num=9, title="CLOSING / BRAND CTA", duration="~14 sec", accent=NAVY,
narration=(
"That's your complete monsoon hearing aid care guide. Five minutes of care each night can add "
"months — even years — to the life of your device. Save this reel, share it with a family "
"member who wears hearing aids, and follow us for more tips from our clinic. And if you have "
"any concerns this monsoon — our door is always open. Your hearing matters."
),
prod_notes=[
"📱 On-screen text: 'Save · Share · Follow' button matches the slide's CTA button.",
"🎨 Slide shows: Full navy background, large ✓ badge, takeaway bullets, clinic name.",
"🎵 Music: Warm closing music swells gently and fades out completely.",
"🎥 End on a smile and a nod to camera — warm, reassuring close.",
"🗣 'Your hearing matters' — final line, deliver with warmth and directness.",
"📌 Replace '[Your Clinic Name]' with your actual clinic name before publishing.",
"🔗 Add your clinic's contact details / website link in the post caption.",
],
tip_text=(
"Record a 2-second pause after the last line before cutting — makes editing cleaner "
"and gives viewers time to process the CTA."
)
))
# ── PRODUCTION SUMMARY PAGE ────────────────────────────────────────────────────
story.append(PageBreak())
story.append(Spacer(1, 0.3*cm))
# Page title
title_rows = [[Paragraph("Production Summary & Publishing Checklist",
ParagraphStyle("PT", fontName="Helvetica-Bold", fontSize=18,
textColor=white, alignment=TA_CENTER))]]
story.append(Table(title_rows, colWidths=[W-3.6*cm],
style=TableStyle([
("BACKGROUND", (0,0), (-1,-1), NAVY),
("TOPPADDING", (0,0), (-1,-1), 12),
("BOTTOMPADDING", (0,0), (-1,-1), 12),
("LEFTPADDING", (0,0), (-1,-1), 10),
])))
story.append(Spacer(1, 0.3*cm))
# Timing table
story.append(Paragraph("SLIDE-BY-SLIDE TIMING BREAKDOWN",
ParagraphStyle("SL", fontName="Helvetica-Bold", fontSize=9, textColor=TEAL_MED,
spaceBefore=6, spaceAfter=4)))
timing_rows = [
["Slide", "Title", "Duration", "Cumulative"],
["1", "Cover", "~7 sec", "0:07"],
["2", "Why Monsoon Is Dangerous", "~12 sec", "0:19"],
["3", "General Rules", "~14 sec", "0:33"],
["4", "Battery-Operated Aid Care", "~16 sec", "0:49"],
["5", "Rechargeable Aid Care", "~16 sec", "1:05"],
["6", "Drying Tools Guide", "~14 sec", "1:19"],
["7", "Emergency — Aid Got Wet", "~18 sec", "1:37"],
["8", "Daily Checklist", "~12 sec", "1:49"],
["9", "Closing / Brand CTA", "~14 sec", "~1:63"],
["", "TOTAL NARRATION TIME", "~103 sec","~1 min 43 sec"],
]
t_style = TableStyle([
("BACKGROUND", (0,0), (-1,0), TEAL),
("TEXTCOLOR", (0,0), (-1,0), white),
("FONTNAME", (0,0), (-1,0), "Helvetica-Bold"),
("FONTSIZE", (0,0), (-1,-1), 9.5),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING", (0,0), (-1,-1), 5),
("LEFTPADDING", (0,0), (-1,-1), 8),
("GRID", (0,0), (-1,-1), 0.4, RULE),
("ROWBACKGROUNDS",(0,1), (-1,-2), [white, TEAL_PALE]),
("BACKGROUND", (0,-1), (-1,-1), NAVY),
("TEXTCOLOR", (0,-1), (-1,-1), white),
("FONTNAME", (0,-1), (-1,-1), "Helvetica-Bold"),
("VALIGN", (0,0), (-1,-1), "MIDDLE"),
])
col_w = [(W-3.6*cm)*f for f in [0.08, 0.46, 0.23, 0.23]]
story.append(Table(timing_rows, colWidths=col_w, style=t_style))
story.append(Spacer(1, 0.4*cm))
# Pre-publish checklist
story.append(Paragraph("PRE-PUBLISH CHECKLIST",
ParagraphStyle("SL2", fontName="Helvetica-Bold", fontSize=9, textColor=TEAL_MED,
spaceBefore=4, spaceAfter=4)))
publish_checks = [
"Replace all '[Your Clinic Name]' placeholders with your actual clinic name on slides",
"Record narration audio — full read-through should be ~90–105 seconds",
"Add background music track (royalty-free recommended — see platforms below)",
"Sync slide transitions to match narration pace (use CapCut / InShot / Canva)",
"Enable auto-captions for accessibility and silent-viewing audiences",
"Add hook text overlay on Slide 1: 'Is monsoon damaging your hearing aid? 👇'",
"Add 'SCREENSHOT THIS' overlay on Slide 8 (checklist slide)",
"Add clinic contact details / website in the post caption",
"Include relevant hashtags in the caption (see below)",
"Test the full reel on mobile before posting — check text readability",
"Schedule post for high-engagement time slot (typically 7–9 AM or 7–9 PM)",
]
for chk in publish_checks:
chk_row = [[
Paragraph("☐", ParagraphStyle("CB", fontName="Helvetica-Bold", fontSize=11,
textColor=TEAL, leading=14)),
Paragraph(chk, ParagraphStyle("CT", fontName="Helvetica", fontSize=9.5,
textColor=SLATE, leading=14))
]]
story.append(Table(chk_row, colWidths=[0.5*cm, W-3.6*cm-0.5*cm],
style=TableStyle([
("TOPPADDING", (0,0), (-1,-1), 3),
("BOTTOMPADDING", (0,0), (-1,-1), 3),
("LEFTPADDING", (0,0), (-1,-1), 2),
("VALIGN", (0,0), (-1,-1), "TOP"),
])))
story.append(Spacer(1, 0.3*cm))
# Hashtag suggestions
story.append(Paragraph("SUGGESTED HASHTAGS",
ParagraphStyle("SL3", fontName="Helvetica-Bold", fontSize=9, textColor=TEAL_MED,
spaceBefore=4, spaceAfter=4)))
hashtag_text = (
"#HearingAidCare #MonsoonCare #HearingHealth #HearingAid #Audiology "
"#RechargeableHearingAid #HearingLoss #HearingAidTips #EarCare "
"#ClinicalAudiology #PatientEducation #HearingAidMaintenance "
"#MonsoonSeason #HearingCareTips #AudiologyClinic"
)
story.append(Table([[Paragraph(hashtag_text,
ParagraphStyle("HT", fontName="Helvetica", fontSize=9.5,
textColor=TEAL, leading=15))]],
colWidths=[W-3.6*cm],
style=TableStyle([
("BACKGROUND", (0,0), (-1,-1), TEAL_PALE),
("BOX", (0,0), (-1,-1), 1, RULE),
("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(Spacer(1, 0.3*cm))
# Music sources
story.append(Paragraph("ROYALTY-FREE MUSIC SOURCES",
ParagraphStyle("SL4", fontName="Helvetica-Bold", fontSize=9, textColor=TEAL_MED,
spaceBefore=4, spaceAfter=4)))
music_rows = [
["Platform", "URL", "Best For"],
["YouTube Audio Library", "studio.youtube.com/channel/music", "Free, no attribution required"],
["Pixabay Music", "pixabay.com/music", "Free, commercial use allowed"],
["Uppbeat", "uppbeat.io", "Clinical/calm tracks, free tier"],
["Epidemic Sound", "epidemicsound.com", "Subscription, professional quality"],
]
m_style = TableStyle([
("BACKGROUND", (0,0), (-1,0), SLATE),
("TEXTCOLOR", (0,0), (-1,0), white),
("FONTNAME", (0,0), (-1,0), "Helvetica-Bold"),
("FONTSIZE", (0,0), (-1,-1), 9),
("TOPPADDING", (0,0), (-1,-1), 4),
("BOTTOMPADDING", (0,0), (-1,-1), 4),
("LEFTPADDING", (0,0), (-1,-1), 8),
("GRID", (0,0), (-1,-1), 0.4, RULE),
("ROWBACKGROUNDS",(0,1), (-1,-1), [white, LIGHT_GREY]),
("TEXTCOLOR", (0,1), (-1,-1), SLATE),
("VALIGN", (0,0), (-1,-1), "MIDDLE"),
])
m_widths = [(W-3.6*cm)*f for f in [0.22, 0.44, 0.34]]
story.append(Table(music_rows, colWidths=m_widths, style=m_style))
story.append(Spacer(1, 0.4*cm))
# Footer note
footer_note = [[Paragraph(
"<b>Confidentiality note:</b> This document is prepared for internal clinic and team use only. "
"The narration script and production notes are intended to guide recording, editing, and "
"publishing of the hearing aid monsoon care reel. Do not distribute externally without "
"replacing all placeholder text with your clinic's details.",
ParagraphStyle("FN", fontName="Helvetica", fontSize=9, textColor=SLATE,
leading=13, alignment=TA_JUSTIFY))]]
story.append(Table(footer_note, colWidths=[W-3.6*cm],
style=TableStyle([
("BACKGROUND", (0,0), (-1,-1), LIGHT_GREY),
("BOX", (0,0), (-1,-1), 1, RULE),
("TOPPADDING", (0,0), (-1,-1), 8),
("BOTTOMPADDING", (0,0), (-1,-1), 8),
("LEFTPADDING", (0,0), (-1,-1), 10),
("RIGHTPADDING", (0,0), (-1,-1), 10),
])))
# ── Build ──────────────────────────────────────────────────────────────────────
doc.build(story, onFirstPage=on_first_page, onLaterPages=on_later_pages)
print(f"Saved: {out}")
cd /tmp/workspace/hearing-aid-guide && python create_script_pdf.py 2>&1
ls -lh /tmp/workspace/hearing-aid-guide/
Hearing Aid Monsoon Video Script
PDF Document · PDF
| Element | What it contains |
|---|---|
| Slide header band | Slide number, title, and estimated duration |
| NARRATION box | Exact word-for-word script in a shaded teal card |
| PRODUCTION NOTES | Music cues, on-screen text overlays, B-roll suggestions, camera direction, tone guidance |
| Green tip boxes | Speaker coaching tips (e.g. "Use a drying box as a prop on camera") |
| Red warning boxes | Critical delivery notes (e.g. "Never charge a wet aid — most emphatic line") |
| File | Purpose |
|---|---|
Hearing Aid Monsoon Care Guide PDF Document · PDF | Share with patients & family |
Hearing Aid Monsoon Branded Reel PDF Document · PDF | Post as Instagram carousel |
Hearing Aid Monsoon Video Script PDF Document · PDF | Internal team production guide |