~/hypothyroidism-pdf/generate_pdf.py
from reportlab.lib.pagesizes import A4
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.units import cm
from reportlab.lib import colors
from reportlab.platypus import (
SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
HRFlowable, PageBreak, KeepTogether
)
from reportlab.lib.enums import TA_LEFT, TA_CENTER, TA_JUSTIFY
OUTPUT = "/home/daytona/workspace/hypothyroidism-pdf/Pharmacotherapy_of_Hypothyroidism.pdf"
doc = SimpleDocTemplate(
OUTPUT,
pagesize=A4,
rightMargin=2*cm, leftMargin=2*cm,
topMargin=2.5*cm, bottomMargin=2.5*cm,
title="Pharmacotherapy of Hypothyroidism",
author="Orris Medical Reference"
)
W = A4[0] - 4*cm # usable width
# ── Styles ──────────────────────────────────────────────────────────────────
base = getSampleStyleSheet()
TEAL = colors.HexColor("#0D6E6E")
TEAL_DARK = colors.HexColor("#094F4F")
TEAL_LITE = colors.HexColor("#E6F4F4")
GRAY_LINE = colors.HexColor("#CCCCCC")
BLACK = colors.black
WHITE = colors.white
title_style = ParagraphStyle("DocTitle",
fontSize=22, leading=28, textColor=WHITE,
fontName="Helvetica-Bold", alignment=TA_CENTER, spaceAfter=4)
subtitle_style = ParagraphStyle("DocSub",
fontSize=10, leading=14, textColor=colors.HexColor("#CCE8E8"),
fontName="Helvetica", alignment=TA_CENTER, spaceAfter=0)
h1_style = ParagraphStyle("H1",
fontSize=14, leading=18, textColor=WHITE,
fontName="Helvetica-Bold", alignment=TA_LEFT,
spaceBefore=10, spaceAfter=4,
leftIndent=0)
h2_style = ParagraphStyle("H2",
fontSize=11, leading=15, textColor=TEAL_DARK,
fontName="Helvetica-Bold", alignment=TA_LEFT,
spaceBefore=8, spaceAfter=3)
h3_style = ParagraphStyle("H3",
fontSize=10, leading=14, textColor=TEAL_DARK,
fontName="Helvetica-BoldOblique", alignment=TA_LEFT,
spaceBefore=6, spaceAfter=2)
body_style = ParagraphStyle("Body",
fontSize=9.5, leading=14, textColor=BLACK,
fontName="Helvetica", alignment=TA_JUSTIFY,
spaceBefore=2, spaceAfter=4)
bullet_style = ParagraphStyle("Bullet",
fontSize=9.5, leading=13, textColor=BLACK,
fontName="Helvetica", alignment=TA_LEFT,
leftIndent=14, firstLineIndent=-10,
spaceBefore=1, spaceAfter=1)
note_style = ParagraphStyle("Note",
fontSize=9, leading=13, textColor=colors.HexColor("#333333"),
fontName="Helvetica-Oblique", alignment=TA_LEFT,
leftIndent=10, rightIndent=10,
spaceBefore=4, spaceAfter=4,
borderPad=5)
source_style = ParagraphStyle("Source",
fontSize=8.5, leading=12, textColor=colors.HexColor("#555555"),
fontName="Helvetica-Oblique", alignment=TA_CENTER,
spaceBefore=6, spaceAfter=2)
# ── Helpers ──────────────────────────────────────────────────────────────────
def h1_block(text):
"""Section header with teal background bar."""
tbl = Table([[Paragraph(text, h1_style)]], colWidths=[W])
tbl.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), TEAL),
("LEFTPADDING", (0,0), (-1,-1), 8),
("RIGHTPADDING", (0,0), (-1,-1), 8),
("TOPPADDING", (0,0), (-1,-1), 6),
("BOTTOMPADDING",(0,0), (-1,-1), 6),
("ROUNDEDCORNERS", [4]),
]))
return tbl
def simple_table(headers, rows, col_widths=None):
"""Styled two-tone table."""
if col_widths is None:
n = len(headers)
col_widths = [W/n]*n
data = [[Paragraph(f"<b>{h}</b>", ParagraphStyle("TH",
fontSize=9, fontName="Helvetica-Bold",
textColor=WHITE, alignment=TA_LEFT)) for h in headers]]
for r in rows:
data.append([Paragraph(str(c), ParagraphStyle("TD",
fontSize=9, fontName="Helvetica",
textColor=BLACK, alignment=TA_LEFT, leading=12)) for c in r])
t = Table(data, colWidths=col_widths, repeatRows=1)
style = [
("BACKGROUND", (0,0), (-1,0), TEAL),
("GRID", (0,0), (-1,-1), 0.4, GRAY_LINE),
("TOPPADDING", (0,0), (-1,-1), 4),
("BOTTOMPADDING", (0,0), (-1,-1), 4),
("LEFTPADDING", (0,0), (-1,-1), 5),
("RIGHTPADDING", (0,0), (-1,-1), 5),
("VALIGN", (0,0), (-1,-1), "TOP"),
]
for i in range(1, len(data)):
bg = TEAL_LITE if i % 2 == 0 else WHITE
style.append(("BACKGROUND", (0,i), (-1,i), bg))
t.setStyle(TableStyle(style))
return t
def note_box(text):
"""Highlighted note / callout box."""
tbl = Table([[Paragraph(f"<b>Note:</b> {text}", note_style)]], colWidths=[W])
tbl.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), colors.HexColor("#FFF8E1")),
("LINEAFTER", (0,0), (0,-1), 2, colors.HexColor("#F5A623")),
("LEFTPADDING", (0,0), (-1,-1), 8),
("RIGHTPADDING", (0,0), (-1,-1), 8),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING", (0,0), (-1,-1), 5),
]))
return tbl
def warning_box(text):
tbl = Table([[Paragraph(f"<b>Warning:</b> {text}", note_style)]], colWidths=[W])
tbl.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), colors.HexColor("#FDECEA")),
("LINEAFTER", (0,0), (0,-1), 2, colors.red),
("LEFTPADDING", (0,0), (-1,-1), 8),
("RIGHTPADDING", (0,0), (-1,-1), 8),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING", (0,0), (-1,-1), 5),
]))
return tbl
def b(txt): return f"<b>{txt}</b>"
def i(txt): return f"<i>{txt}</i>"
# ── Content ──────────────────────────────────────────────────────────────────
story = []
# ── TITLE BANNER ──
title_banner = Table(
[[Paragraph("Pharmacotherapy of Hypothyroidism", title_style)],
[Paragraph("Based on Katzung | Goodman & Gilman | Tietz Laboratory Medicine", subtitle_style)]],
colWidths=[W]
)
title_banner.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), TEAL_DARK),
("TOPPADDING", (0,0), (-1,-1), 16),
("BOTTOMPADDING", (0,0), (-1,-1), 16),
("LEFTPADDING", (0,0), (-1,-1), 10),
("RIGHTPADDING", (0,0), (-1,-1), 10),
]))
story.append(title_banner)
story.append(Spacer(1, 0.4*cm))
# ── SECTION 1 ──
story.append(h1_block("1. Therapeutic Preparations"))
story.append(Spacer(1, 0.2*cm))
story.append(simple_table(
["Preparation", "Contents", "Status"],
[
["Levothyroxine (T4)", "Synthetic L-thyroxine", "Drug of choice"],
["Liothyronine (T3)", "Synthetic L-triiodothyronine", "Adjunct / emergency use"],
["Desiccated thyroid extract (DTE)", "Porcine T4 + T3 (~4:1 ratio)", "Occasional patient preference"],
],
col_widths=[W*0.33, W*0.37, W*0.30]
))
story.append(Spacer(1, 0.3*cm))
# ── SECTION 2 ──
story.append(h1_block("2. Levothyroxine — Drug of Choice"))
story.append(Spacer(1, 0.2*cm))
story.append(Paragraph(
"Levothyroxine sodium (synthetic L-thyroxine, T4) is universally recommended as first-line therapy "
"for all forms of hypothyroidism by the American Thyroid Association (ATA) and European Thyroid "
"Association (ETA).",
body_style
))
story.append(Paragraph(b("Why T4 is preferred over T3 or DTE:"), h3_style))
for pt in [
"Consistent, predictable potency and chemical stability",
"Long plasma half-life (~7 days) — enables once-daily dosing",
"Peripheral deiodination (Dio1, Dio2) converts T4 → active T3, closely mimicking normal physiology",
"~80% of circulating T3 derives from peripheral T4 conversion",
"Multiple controlled trials confirm no inferiority compared to T4+T3 combination",
]:
story.append(Paragraph(f"• {pt}", bullet_style))
story.append(Spacer(1, 0.3*cm))
# ── SECTION 3 ──
story.append(h1_block("3. Pharmacokinetics of Levothyroxine"))
story.append(Spacer(1, 0.2*cm))
story.append(simple_table(
["Parameter", "Detail"],
[
["Absorption site", "Proximal small bowel"],
["Bioavailability (fasting)", "60–80%"],
["Time to peak concentration", "~2 h (fasting); ~3 h in hypothyroid state"],
["Plasma half-life", "~7 days (extensive TBG, TBPA, albumin binding)"],
["Metabolism", "Peripheral deiodination → T3 (active) or rT3 (inactive); hepatic conjugation"],
["Steady state", "6–8 weeks after a consistent dose"],
["Formulation", "Oral tablets (standard); IV available for emergencies"],
],
col_widths=[W*0.38, W*0.62]
))
story.append(Spacer(1, 0.3*cm))
# ── SECTION 4 ──
story.append(h1_block("4. Dosing"))
story.append(Spacer(1, 0.2*cm))
story.append(Paragraph(b("Standard Adult Dosing"), h2_style))
story.append(simple_table(
["Indication", "Dose"],
[
["Average full replacement (adults)", "~1.7 mcg/kg/day (~125 mcg/day for 70 kg)"],
["Older adults (>65 years)", "~1.6 mcg/kg/day"],
["Post-thyroidectomy TSH suppression (thyroid cancer)", "~2.2 mcg/kg/day"],
],
col_widths=[W*0.55, W*0.45]
))
story.append(Spacer(1, 0.25*cm))
story.append(Paragraph(b("Age-Specific Dosing"), h2_style))
story.append(simple_table(
["Age Group", "Dose (mcg/kg/day)"],
[
["Infants 1–6 months", "10–15"],
["Children 6–12 months", "6–8"],
["Children 1–5 years", "5–6"],
["Adolescents (>12 years)", "2–3"],
["Adults", "~1.7"],
["Elderly (>65 years)", "~1.6"],
],
col_widths=[W*0.55, W*0.45]
))
story.append(Spacer(1, 0.25*cm))
story.append(Paragraph(b("Initiating Therapy"), h2_style))
story.append(simple_table(
["Patient Type", "Starting Dose", "Titration"],
[
["Young / mild disease", "Full replacement immediately", "Check TSH at 6–8 weeks"],
["Age >50 yr, no cardiac disease", "50 mcg/day", "Increase to target"],
["Elderly / long-standing / cardiac disease", "12.5–25 mcg/day", "Increase by 12.5–25 mcg every 2 weeks"],
],
col_widths=[W*0.36, W*0.32, W*0.32]
))
story.append(Spacer(1, 0.2*cm))
story.append(warning_box(
"In coronary artery disease, low thyroid hormone levels protect the heart from increased oxygen demand. "
"Overly rapid T4 correction can precipitate angina, arrhythmia, or MI. "
"Perform coronary revascularization before aggressive T4 replacement if both are needed."
))
story.append(Spacer(1, 0.3*cm))
# ── SECTION 5 ──
story.append(h1_block("5. Monitoring"))
story.append(Spacer(1, 0.2*cm))
story.append(simple_table(
["Type", "Monitor", "Target", "Timing"],
[
["Primary hypothyroidism", "Serum TSH", "0.5–2.5 mIU/L", "6–8 wk post-change; then 4–6 months; then yearly"],
["Secondary/tertiary (central)", "Free T4 (TSH unreliable)", "Upper third of reference range", "Same intervals"],
["Children", "TSH + Free T4 + growth", "Age-appropriate", "More frequent"],
["Pregnancy", "TSH (primary guide)", "Trimester-specific", "Every 4–6 wk in first 20 wk"],
],
col_widths=[W*0.22, W*0.22, W*0.26, W*0.30]
))
story.append(Spacer(1, 0.3*cm))
# ── SECTION 6 ──
story.append(h1_block("6. Administration Pearls"))
story.append(Spacer(1, 0.2*cm))
story.append(Paragraph(
"Take on an empty stomach — 30–60 min before breakfast <b>or</b> at bedtime (4 h after last meal). "
"Food and caffeine delay absorption.",
body_style
))
story.append(Paragraph(b("Substances Reducing Absorption (separate by ≥4 hours):"), h3_style))
for pt in [
"Calcium carbonate, iron salts (ferrous sulfate)",
"Cholestyramine, colestipol",
"Proton pump inhibitors, sucralfate, antacids (Al/Mg)",
"Soy products, dietary bran/fiber, coffee",
]:
story.append(Paragraph(f"• {pt}", bullet_style))
story.append(Paragraph(b("Conditions Requiring Higher Doses:"), h3_style))
for pt in [
"Malabsorption: celiac disease, atrophic gastritis, H. pylori gastritis, lactose intolerance",
"Post-bariatric surgery / small bowel resection",
"Pregnancy (increased TBG, Dio3 expression by placenta)",
"Enzyme-inducing drugs (rifampin, phenytoin, carbamazepine)",
]:
story.append(Paragraph(f"• {pt}", bullet_style))
story.append(Spacer(1, 0.3*cm))
# ── SECTION 7 ──
story.append(h1_block("7. Special Clinical Situations"))
story.append(Spacer(1, 0.2*cm))
story.append(Paragraph("A. Subclinical Hypothyroidism (SCH)", h2_style))
story.append(Paragraph(
"Defined as elevated TSH + normal free T4. Prevalence: 4–10% general population; up to 20% in women >50 years.",
body_style
))
story.append(simple_table(
["TSH Level", "Age", "Recommendation"],
[
[">10 mIU/L", "<65–70 years", "Levothyroxine recommended"],
["<10 mIU/L with symptoms", "Any", "Consider trial; stop if no improvement"],
["≤10 mIU/L", ">80–85 years", "Watchful waiting"],
],
col_widths=[W*0.28, W*0.24, W*0.48]
))
story.append(Spacer(1, 0.25*cm))
story.append(Paragraph("B. Hypothyroidism in Pregnancy", h2_style))
story.append(Paragraph(
"Overt hypothyroidism: risk of miscarriage, preterm birth, fetal distress, impaired fetal neurodevelopment.",
body_style
))
for pt in [
"Increase levothyroxine by ~25–30% as soon as pregnancy confirmed (practical: 2 extra tablets/week)",
"TSH targets: 1st trimester 0.1–2.5 mIU/L; 2nd trimester 0.2–3.0 mIU/L; 3rd trimester 0.3–3.0 mIU/L",
"Separate T4 from prenatal vitamins/calcium by ≥4 hours",
"Revert to pre-pregnancy dose the day after delivery; recheck TSH at 6 weeks postpartum",
]:
story.append(Paragraph(f"• {pt}", bullet_style))
story.append(Spacer(1, 0.25*cm))
story.append(Paragraph("C. Myxedema Coma — Emergency", h2_style))
story.append(Paragraph(
i("Rare, life-threatening extreme of untreated hypothyroidism. Precipitants: infection, heart failure, "
"non-compliance. Most common in elderly women in winter months."),
body_style
))
story.append(Paragraph(b("Cardinal features: ") + "hypothermia, respiratory depression, decreased consciousness, "
"hyponatremia, hypoglycemia, shock", body_style))
story.append(simple_table(
["Drug", "Dose", "Route"],
[
["Levothyroxine (loading)", "300–400 mcg, then 50–100 mcg/day", "IV (GI absorption unreliable)"],
["Liothyronine (T3)", "5–20 mcg initially, then 2.5–10 mcg every 8 h", "IV (optional; more cardiotoxic)"],
["Hydrocortisone", "50–100 mg every 6–8 h", "IV (until adrenal insufficiency excluded)"],
],
col_widths=[W*0.28, W*0.45, W*0.27]
))
story.append(Spacer(1, 0.15*cm))
story.append(note_box(
"All drugs must be given IV — GI absorption is unreliable in myxedema coma. "
"ICU care, mechanical ventilation if needed, passive rewarming, cautious IV fluids (SIADH risk). "
"Opioids and sedatives must be used with extreme caution."
))
story.append(Spacer(1, 0.25*cm))
story.append(Paragraph("D. Congenital Hypothyroidism", h2_style))
for pt in [
"Treatment within first 2 weeks of life → normal physical and intellectual development",
"Delayed diagnosis → cretinism (irreversible cognitive impairment, short stature)",
"Initial dose: 10–15 mcg/kg/day orally (crushed tablet in breast milk/water); higher end for severe cases",
"Monitor free T4 + TSH every 2 weeks initially, then every 1–3 months in the first year",
"Soy formula impairs absorption — dose increase may be needed",
]:
story.append(Paragraph(f"• {pt}", bullet_style))
story.append(Spacer(1, 0.25*cm))
story.append(Paragraph("E. Drug-Induced Hypothyroidism", h2_style))
story.append(simple_table(
["Drug", "Mechanism"],
[
["Amiodarone", "Inhibits T4→T3 conversion; high iodine load; direct thyroid toxicity"],
["Lithium", "Inhibits thyroid hormone release (~30% develop elevated TSH)"],
["Interferon-alpha", "Induces autoimmune thyroiditis"],
["Tyrosine kinase inhibitors", "Impair thyroid hormone synthesis/release"],
["Immune checkpoint inhibitors", "Autoimmune thyroiditis"],
],
col_widths=[W*0.38, W*0.62]
))
story.append(Spacer(1, 0.1*cm))
story.append(note_box(
"Amiodarone: Levothyroxine replacement may be required even after stopping, "
"due to amiodarone's extremely long half-life (~40–55 days)."
))
story.append(Spacer(1, 0.25*cm))
story.append(Paragraph("F. Central (Secondary/Tertiary) Hypothyroidism", h2_style))
for pt in [
"Monitor with free T4, not TSH (pituitary TSH secretion is abnormal)",
"Goal: maintain free T4 in upper third of reference range",
]:
story.append(Paragraph(f"• {pt}", bullet_style))
story.append(warning_box(
"Always exclude coexisting central adrenal insufficiency BEFORE starting levothyroxine. "
"Starting T4 without cortisol replacement can precipitate an adrenal crisis."
))
story.append(Spacer(1, 0.25*cm))
story.append(Paragraph("G. Thyroid Cancer (TSH Suppression)", h2_style))
story.append(simple_table(
["Risk Category", "TSH Target"],
[
["Low-risk / no persistent disease", "Low-normal TSH"],
["High recurrence risk", "~0.1 mU/L"],
["Persistent disease", "<0.1 mU/L"],
],
col_widths=[W*0.5, W*0.5]
))
story.append(Spacer(1, 0.3*cm))
# ── SECTION 8 ──
story.append(h1_block("8. Combination T4 + T3 Therapy"))
story.append(Spacer(1, 0.2*cm))
story.append(Paragraph(
b("ATA/ETA position: ") + "Routine combination levothyroxine + liothyronine is "
+ b("not recommended") + " as standard care.",
body_style
))
story.append(Paragraph(b("Why combination is not routine:"), h3_style))
for pt in [
"No long-acting T3 formulation exists — causes peaks and troughs",
"Existing trials show no consistent superiority over T4 alone",
"No T4/T3 preparation matches the thyroid's natural ~11:1 ratio",
]:
story.append(Paragraph(f"• {pt}", bullet_style))
story.append(Paragraph(b("Where combination may be considered:"), h3_style))
for pt in [
"Patients persistently symptomatic despite normal TSH/free T4 on levothyroxine",
"Possible Dio2 Thr92Ala polymorphism carriers (impaired intracellular T4→T3 conversion)",
"Desiccated thyroid extract (DTE): some patient preference data (crossover studies show weight benefit)",
]:
story.append(Paragraph(f"• {pt}", bullet_style))
story.append(Spacer(1, 0.3*cm))
# ── SECTION 9 ──
story.append(h1_block("9. Drug Interactions with Levothyroxine"))
story.append(Spacer(1, 0.2*cm))
story.append(simple_table(
["Interacting Drug / Substance", "Effect"],
[
["Calcium carbonate, iron salts, PPIs, antacids, sucralfate", "Reduced T4 absorption (separate by ≥4 h)"],
["Cholestyramine, colestipol", "Binds T4 in gut, reduces absorption"],
["Rifampin, phenytoin, carbamazepine", "Increased hepatic T4 metabolism → higher dose needed"],
["Estrogen / oral contraceptives", "Increase TBG → may increase dose requirement"],
["Amiodarone", "Inhibits T4→T3 conversion (can cause hypo- or hyperthyroidism)"],
["Warfarin", "T4 potentiates anticoagulant effect"],
["Soy, bran, coffee", "Reduce gastrointestinal absorption"],
],
col_widths=[W*0.50, W*0.50]
))
story.append(Spacer(1, 0.3*cm))
# ── SECTION 10 ──
story.append(h1_block("10. Toxicity from Overtreatment"))
story.append(Spacer(1, 0.2*cm))
story.append(simple_table(
["Adults", "Children"],
[
["Nervousness, heat intolerance", "Restlessness, insomnia"],
["Palpitations, tachycardia", "Accelerated bone maturation"],
["Unexplained weight loss", "Premature craniosynostosis"],
["Atrial fibrillation (especially elderly)", "Accelerated linear growth"],
["Accelerated osteoporosis", ""],
],
col_widths=[W*0.5, W*0.5]
))
story.append(Spacer(1, 0.15*cm))
story.append(note_box(
"Monitor TSH and free T4 when toxicity symptoms arise. "
"Stop or reduce dose immediately if angina or arrhythmia develops in cardiac patients."
))
story.append(Spacer(1, 0.3*cm))
# ── SECTION 11 ──
story.append(h1_block("11. Recent Evidence (2025–2026)"))
story.append(Spacer(1, 0.2*cm))
story.append(simple_table(
["Study", "Finding"],
[
["Network meta-analysis, J Clin Endocrinol Metab 2025 (PMID 39290156)",
"Examined treatment preferences; evidence for benefit of non-levothyroxine-alone regimens in patient-reported outcomes"],
["Meta-analysis, J Clin Endocrinol Metab 2026 (PMID 40579157)",
"Liothyronine-containing regimens associated with REDUCED RISK of dementia and mortality vs levothyroxine monotherapy"],
],
col_widths=[W*0.42, W*0.58]
))
story.append(Spacer(1, 0.15*cm))
story.append(note_box(
"Current ATA/ETA guidelines still recommend T4 monotherapy as first-line. "
"The 2026 findings may prompt future guideline revision, particularly for symptomatic patients on T4 alone."
))
story.append(Spacer(1, 0.4*cm))
# ── SOURCES ──
story.append(HRFlowable(width=W, thickness=0.5, color=GRAY_LINE))
story.append(Spacer(1, 0.15*cm))
story.append(Paragraph(
"Sources: Katzung's Basic and Clinical Pharmacology, 16th Ed. | "
"Goodman & Gilman's The Pharmacological Basis of Therapeutics | "
"Tietz Textbook of Laboratory Medicine, 7th Ed.",
source_style
))
# ── BUILD ──────────────────────────────────────────────────────────────────
doc.build(story)
print(f"PDF saved to: {OUTPUT}")