Adult vaccination
CDC adult immunization schedule 2026 recommended vaccines by age
"adult immunization" AND "vaccination schedule"
https://www.cdc.gov/vaccines/hcp/imz-schedules/adult-age-com…
adult immunization vaccination schedule recommended vaccines by age
| Vaccine | Recommendation |
|---|---|
| Influenza (IIV3, ccIIV3, RIV3) | 1 dose annually - all adults (¶). For age ≥65: high-dose (HD-IIV3), adjuvanted (aIIV3), or recombinant (RIV3) preferred |
| COVID-19 | ≥1 dose of updated vaccine for ages 19-64; ≥65 yrs: ≥2 doses of updated vaccine |
| Vaccine | Dose | Notes |
|---|---|---|
| Tdap/Td | 1 dose Tdap if never received, then Td/Tdap booster every 10 years | Every pregnancy regardless of prior Tdap |
| MMR | 1-2 doses (if born 1957 or later, unvaccinated/no immunity) | 2 doses if high-risk (healthcare workers, students, travelers) |
| Varicella (VAR) | 2 doses (if born 1980 or later, no evidence of immunity) | 4-8 weeks apart |
| HPV (Gardasil 9) | 2-3 doses (¶ for all through age 26) | 2-dose series if started before age 15; 3-dose if started at 15+ |
| Hepatitis B (HepB) | 2, 3, or 4 doses depending on vaccine | Heplisav-B: 2 doses; Engerix/Recombivax: 3 doses; PreHevbrio: 3 doses |
| Vaccine | Recommendation |
|---|---|
| HPV | 27-45 yrs: based on shared clinical decision-making (±) |
| Hepatitis B | Recommended through age 59 if not previously vaccinated (¶) |
| All others above | As applicable (MMR, Varicella if no immunity) |
| Vaccine | Recommendation |
|---|---|
| Zoster (RZV / Shingrix) | 2 doses (¶) - 2-6 months apart; preferred over live zoster vaccine |
| Pneumococcal | Risk-based §; routine at 65. Some conditions warrant earlier vaccination |
| RSV (Abrysvo, Arexvy, mResvia) | 50-59 yrs with increased risk of severe RSV (§) (new June 2025) |
| Vaccine | Recommendation |
|---|---|
| Pneumococcal (PCV20 or PCV21) | 1 dose PCV20 or PCV21 if no prior pneumococcal vaccine (¶) |
| PCV15 + PPSV23 | If PCV15 used: follow with PPSV23 at least 1 year later |
| Zoster (RZV / Shingrix) | 2 doses if not previously vaccinated (¶) |
| RSV | ≥75 yrs: 1 dose (¶). 60-74 yrs: discuss benefits/risks |
| COVID-19 | ≥2 doses of updated vaccine |
| Indication | Vaccine |
|---|---|
| MenACWY | Asplenia, complement deficiency, complement inhibitor use, HIV, travel to endemic areas, college freshmen in dorms, military recruits |
| MenB | Age 16-23 (±); asplenia, complement deficiency/inhibitor use, microbiologist |
| MenABCWY (Penmenvy) | NEW (June 2025): when both MenACWY and MenB are needed simultaneously |
| Population | Key Vaccines |
|---|---|
| Pregnant | Tdap each pregnancy (27-36 weeks), annual influenza (any trimester), COVID-19, RSV (seasonal, 32-36 weeks gestation) |
| Immunocompromised | Avoid live vaccines (LAIV, MMR, VAR, live zoster); Shingrix OK; extra doses of pneumococcal; Hib if post-transplant |
| Healthcare workers | MMR (2 doses), Varicella (2 doses), Hepatitis B (3 doses), annual flu, Tdap |
| Asplenic/Complement deficiency | Pneumococcal, MenACWY + MenB, Hib |
| Travelers | Hepatitis A, Hepatitis B, Typhoid, Yellow Fever, Meningococcal, Japanese Encephalitis (destination-dependent) |
| If patient missed... | Action |
|---|---|
| Tdap series | 1 dose Tdap now, then Td every 10 yrs |
| Hepatitis B series | Start 3-dose series (can use accelerated: 0, 1, 2 months + booster at 12 months) |
| MMR (born 1957+) | 1 dose; 2 doses if high-risk |
| Varicella (born 1980+) | 2 doses, 4-8 weeks apart |
| Shingrix | 2 doses, 2-6 months apart (8-12 months if immunocompromised) |
| HPV (age ≤26 at start) | 2 doses if series started before age 15; 3 doses if 15+ |
| Contraindication | Vaccines to Avoid |
|---|---|
| Pregnancy | Live vaccines: LAIV, MMR, VAR, live zoster (old Zostavax) |
| Severe immunosuppression | All live vaccines |
| Severe egg allergy | Egg-based IIV3 (use RIV3 or ccIIV3 instead) |
| Anaphylaxis to prior dose | All subsequent doses of that vaccine |
| Active moderate-to-severe illness | Defer until recovery |
Note: This reflects the U.S. ACIP schedule. Always verify the most current schedule with the CDC immunization page for the latest updates, and consider local/regional guidelines where applicable.
Create a reference PDF summary of this schedule for your clinic
ls -la /tmp/workspace/ 2>/dev/null || echo "workspace does not exist yet"
mkdir -p /tmp/workspace/adult-vaccination
~/adult-vaccination/generate_pdf.py
from reportlab.lib.pagesizes import A4, landscape
from reportlab.lib import colors
from reportlab.lib.units import mm, cm
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.platypus import (
SimpleDocTemplate, Table, TableStyle, Paragraph, Spacer,
HRFlowable, KeepTogether
)
from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_RIGHT
from reportlab.platypus import PageBreak
from datetime import date
OUTPUT = "/tmp/workspace/adult-vaccination/Adult_Vaccination_Schedule.pdf"
# ── Colour palette ──────────────────────────────────────────────────────────
NAVY = colors.HexColor("#1A3A5C")
TEAL = colors.HexColor("#1B7A8A")
LTBLUE = colors.HexColor("#D6EAF8")
LTGREEN = colors.HexColor("#D5F5E3")
LTYELL = colors.HexColor("#FEF9E7")
LTORANG = colors.HexColor("#FDEBD0")
LTRED = colors.HexColor("#FADBD8")
LTGRAY = colors.HexColor("#F2F3F4")
WHITE = colors.white
DARKGRAY= colors.HexColor("#555555")
MIDGRAY = colors.HexColor("#888888")
doc = SimpleDocTemplate(
OUTPUT,
pagesize=A4,
leftMargin=15*mm, rightMargin=15*mm,
topMargin=18*mm, bottomMargin=18*mm,
title="Adult Vaccination Schedule - Clinic Reference",
author="Orris Clinical Reference",
subject="ACIP 2025 Adult Immunization Schedule",
)
styles = getSampleStyleSheet()
# Custom styles
H1 = ParagraphStyle("H1", fontName="Helvetica-Bold", fontSize=18,
textColor=WHITE, alignment=TA_CENTER, spaceAfter=4)
H2 = ParagraphStyle("H2", fontName="Helvetica-Bold", fontSize=11,
textColor=WHITE, alignment=TA_LEFT, spaceBefore=4, spaceAfter=2)
H3 = ParagraphStyle("H3", fontName="Helvetica-Bold", fontSize=9,
textColor=NAVY, spaceBefore=6, spaceAfter=2)
BODY = ParagraphStyle("BODY", fontName="Helvetica", fontSize=8,
textColor=DARKGRAY, leading=11)
SMALL = ParagraphStyle("SMALL", fontName="Helvetica", fontSize=7,
textColor=MIDGRAY, leading=9)
BOLD_SMALL = ParagraphStyle("BOLD_SMALL", fontName="Helvetica-Bold", fontSize=7.5,
textColor=NAVY, leading=10)
FOOTER = ParagraphStyle("FOOTER", fontName="Helvetica-Oblique", fontSize=6.5,
textColor=MIDGRAY, alignment=TA_CENTER)
LEGEND = ParagraphStyle("LEGEND", fontName="Helvetica", fontSize=7.5,
textColor=DARKGRAY, leading=11)
def section_header(text, bg=NAVY):
tbl = Table([[Paragraph(text, H2)]], colWidths=[180*mm])
tbl.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), bg),
("LEFTPADDING", (0,0), (-1,-1), 6),
("RIGHTPADDING", (0,0), (-1,-1), 6),
("TOPPADDING", (0,0), (-1,-1), 4),
("BOTTOMPADDING", (0,0), (-1,-1), 4),
("ROUNDEDCORNERS", [3, 3, 3, 3]),
]))
return tbl
def make_table(headers, rows, col_widths, row_colors=None):
data = [[Paragraph(f"<b>{h}</b>", BOLD_SMALL) for h in headers]]
for row in rows:
data.append([Paragraph(str(cell), BODY) for cell in row])
style = [
("BACKGROUND", (0,0), (-1,0), NAVY),
("TEXTCOLOR", (0,0), (-1,0), WHITE),
("FONTNAME", (0,0), (-1,0), "Helvetica-Bold"),
("FONTSIZE", (0,0), (-1,0), 7.5),
("ROWBACKGROUNDS", (0,1), (-1,-1), [WHITE, LTGRAY]),
("GRID", (0,0), (-1,-1), 0.3, colors.HexColor("#CCCCCC")),
("VALIGN", (0,0), (-1,-1), "TOP"),
("LEFTPADDING",(0,0), (-1,-1), 4),
("RIGHTPADDING",(0,0), (-1,-1), 4),
("TOPPADDING", (0,0), (-1,-1), 3),
("BOTTOMPADDING",(0,0), (-1,-1), 3),
]
if row_colors:
for (r, c) in row_colors:
style.append(("BACKGROUND", (0, r), (-1, r), c))
tbl = Table(data, colWidths=col_widths, repeatRows=1)
tbl.setStyle(TableStyle(style))
return tbl
# ── Build story ─────────────────────────────────────────────────────────────
story = []
# ── TITLE BANNER ─────────────────────────────────────────────────────────────
banner_data = [[
Paragraph("ADULT VACCINATION SCHEDULE", H1),
Paragraph(f"<font color='#AADDFF' size=8>ACIP 2025 | Updated June 2025<br/>Clinic Reference • {date.today().strftime('%B %d, %Y')}</font>",
ParagraphStyle("sub", fontName="Helvetica", fontSize=8, textColor=colors.HexColor("#AADDFF"), alignment=TA_RIGHT, leading=11))
]]
banner = Table(banner_data, colWidths=[120*mm, 60*mm])
banner.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), NAVY),
("LEFTPADDING", (0,0), (-1,-1), 8),
("RIGHTPADDING", (0,0), (-1,-1), 8),
("TOPPADDING", (0,0), (-1,-1), 8),
("BOTTOMPADDING",(0,0), (-1,-1), 8),
("VALIGN", (0,0), (-1,-1), "MIDDLE"),
]))
story.append(banner)
story.append(Spacer(1, 3*mm))
# ── LEGEND ──────────────────────────────────────────────────────────────────
legend_items = [
["¶ Routine", "Recommended for all adults meeting age/criteria who lack documentation or evidence of immunity"],
["§ Risk-based", "Recommended for adults with specific risk factors or indications"],
["± Shared decision", "Discuss with patient; either option clinically acceptable"],
]
leg_tbl = Table(
[[Paragraph(f"<b><font color='#1A3A5C'>{k}</font></b> <font size=7 color='#555555'>{v}</font>", LEGEND)
for k, v in legend_items]],
colWidths=[60*mm, 60*mm, 60*mm]
)
leg_tbl.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), LTBLUE),
("BOX", (0,0), (-1,-1), 0.5, TEAL),
("LEFTPADDING", (0,0), (-1,-1), 5),
("RIGHTPADDING", (0,0), (-1,-1), 5),
("TOPPADDING", (0,0), (-1,-1), 4),
("BOTTOMPADDING",(0,0), (-1,-1), 4),
]))
story.append(leg_tbl)
story.append(Spacer(1, 4*mm))
# ════════════════════════════════════════════════════════════════════════════
# SECTION 1 — ANNUAL VACCINES
# ════════════════════════════════════════════════════════════════════════════
story.append(section_header("1. Annual Vaccines — All Adults Every Year", TEAL))
story.append(Spacer(1, 2*mm))
annual_rows = [
["Influenza\n(IIV3, ccIIV3, RIV3)", "1 dose annually ¶",
"All adults. Age ≥65: prefer HD-IIV3 (Fluzone High-Dose), aIIV3 (Fluad), or RIV3 (Flublok).\nLAIV (FluMist) NOT recommended age ≥50 or immunocompromised."],
["COVID-19", "≥1 dose updated vaccine ¶\n≥65 yrs: ≥2 doses ¶",
"Use current season's updated formulation. Timing per current CDC guidance."],
]
story.append(make_table(
["Vaccine", "Dose", "Notes"],
annual_rows,
[38*mm, 42*mm, 100*mm]
))
story.append(Spacer(1, 4*mm))
# ════════════════════════════════════════════════════════════════════════════
# SECTION 2 — SCHEDULE BY AGE GROUP
# ════════════════════════════════════════════════════════════════════════════
story.append(section_header("2. Schedule by Age Group"))
story.append(Spacer(1, 2*mm))
age_rows = [
# (age group label, vaccine, dose, indication, notes)
["19–26 yrs", "Tdap / Td", "1 dose Tdap; then Td/Tdap every 10 yrs ¶", "All", "Every pregnancy at 27–36 wks regardless of prior Tdap. Td for wound management."],
["19–26 yrs", "MMR", "1–2 doses (born ≥1957) ¶", "All without evidence of immunity", "2 doses if HCW, student, traveler, or outbreak. 4 weeks apart."],
["19–26 yrs", "Varicella (VAR)", "2 doses (born ≥1980) ¶", "No evidence of immunity", "4–8 weeks apart. Do NOT give to pregnant or severely immunocompromised."],
["19–26 yrs", "HPV (Gardasil 9)", "2 or 3 doses ¶", "All through age 26", "2-dose if series started <15 yrs; 3-dose if started ≥15 yrs or immunocompromised."],
["19–59 yrs", "Hepatitis B (HepB)", "2–4 doses ¶", "All through age 59", "Heplisav-B: 2 doses at 0, 1 mo. Engerix/Recombivax: 3 doses. Twinrix: 3 doses."],
["27–45 yrs", "HPV (Gardasil 9)", "2 or 3 doses ±", "Shared decision-making", "Benefit decreasing with age; discuss prior exposure, new partners, immune status."],
["50+ yrs", "Zoster (RZV / Shingrix)", "2 doses ¶", "All ≥50 yrs", "0 and 2–6 months apart (or 2–12 months). Immunocompromised: 2–12 months apart.\nPreferred over old live zoster (Zostavax, discontinued)."],
["50–59 yrs", "RSV (Abrysvo/Arexvy/mResvia)", "1 dose §", "Increased risk of severe RSV", "NEW June 2025. Risk factors include COPD, HF, diabetes, obesity, immunosuppression. Single dose only; no repeat doses."],
["60–74 yrs", "RSV", "1 dose ±", "Discuss risks/benefits", "Shared clinical decision; strongly consider those with comorbidities."],
["≥65 yrs", "RSV", "1 dose ¶", "All ≥75 yrs; 60–74 with risk factors", "Administer before RSV season (fall/early winter)."],
["≥65 yrs", "Pneumococcal", "1 dose PCV20 or PCV21 ¶", "All, no prior pneumococcal vax", "OR: PCV15 followed by PPSV23 ≥1 year later. See risk-based section for earlier vaccination."],
["≥65 yrs", "Influenza (high-dose)", "1 dose annually ¶", "All ≥65", "HD-IIV3, aIIV3, or RIV3 preferred over standard-dose."],
["≥65 yrs", "COVID-19", "≥2 doses updated vaccine ¶", "All", "Higher-risk; ensure current season's formulation."],
]
story.append(make_table(
["Age Group", "Vaccine", "Dose", "Indication", "Key Notes"],
age_rows,
[18*mm, 33*mm, 36*mm, 28*mm, 65*mm],
row_colors=[(1,LTBLUE),(2,LTBLUE),(3,LTBLUE),(4,LTBLUE),
(5,LTGREEN),(6,LTGREEN),
(7,LTYELL),(8,LTYELL),(9,LTYELL),(10,LTYELL),
(11,LTORANG),(12,LTORANG),(13,LTORANG),(14,LTORANG)]
))
story.append(Spacer(1, 4*mm))
# ════════════════════════════════════════════════════════════════════════════
# SECTION 3 — RISK-BASED VACCINES
# ════════════════════════════════════════════════════════════════════════════
story.append(section_header("3. Risk-Based Vaccines (Any Age)"))
story.append(Spacer(1, 2*mm))
risk_rows = [
["Pneumococcal\n(PCV20, PCV21, PCV15+PPSV23)",
"Chronic heart/lung/liver/renal disease, DM, alcoholism, smoking, asplenia, immunocompromising conditions, cochlear implant, CSF leak",
"PCV20 or PCV21 × 1; or PCV15 followed by PPSV23 ≥1 yr later"],
["Hepatitis A\n(HepA)",
"Chronic liver disease, clotting disorders, MSM, homelessness, travel to endemic areas, drug users, occupational exposure",
"2, 3, or 4 doses depending on brand. Havrix/Vaqta: 2 doses. Twinrix: 3 doses."],
["Hepatitis B\n(HepB)",
"ESRD/dialysis, HCW with blood exposure, incarceration, HIV, sex partners of HBsAg+, travel",
"≥60 yrs: risk-based §. Dialysis: double-dose (40 mcg) series. Check anti-HBs after vaccination."],
["Meningococcal\nMenACWY",
"Asplenia, complement deficiency or inhibitor use (e.g. eculizumab), HIV, travel to endemic area (e.g. Hajj, meningitis belt), college freshmen in dorms, military recruits",
"1–2 doses; booster every 5 yrs if ongoing risk."],
["Meningococcal B\n(MenB)",
"Asplenia, complement deficiency/inhibitor use, microbiologists working with N. meningitidis; age 16–23 ±",
"Bexsero: 2 doses (0, ≥1 mo). Trumenba: 3 doses (0, 1–2, 6 mo). NOT interchangeable."],
["MenABCWY\n(Penmenvy) — NEW",
"When both MenACWY AND MenB are indicated at same visit:\n• Healthy 16–23 yrs (shared decision)\n• ≥10 yrs at increased meningococcal risk (asplenia, complement def/inhibitor)",
"Single combination vaccine. Approved by ACIP June 25, 2025."],
["Haemophilus b\n(Hib)",
"Post-splenectomy (elective), hematopoietic stem cell transplant recipients",
"1 dose (post-splenectomy) or 3-dose series (HSCT)."],
["Mpox (Jynneos)",
"MSM or bisexual men with multiple/anonymous partners, sex workers, persons in household with Mpox case, lab workers handling orthopoxviruses",
"2 doses SC (0 and 4 wks apart). Immunocompromised: do NOT use intradermal route."],
["Typhoid, Yellow Fever,\nJapanese Encephalitis, Rabies",
"Travelers to endemic regions; specific occupational exposure (rabies)",
"Travel clinic consultation recommended. Yellow fever requires certified vaccination center."],
]
story.append(make_table(
["Vaccine", "Indications", "Dosing / Brand Notes"],
risk_rows,
[38*mm, 78*mm, 64*mm]
))
story.append(Spacer(1, 4*mm))
# ════════════════════════════════════════════════════════════════════════════
# SECTION 4 — SPECIAL POPULATIONS
# ════════════════════════════════════════════════════════════════════════════
story.append(KeepTogether([
section_header("4. Special Populations"),
Spacer(1, 2*mm),
make_table(
["Population", "Key Vaccines", "Special Considerations"],
[
["Pregnant", "Tdap (27–36 wks EVERY pregnancy), Influenza (any trimester), COVID-19, RSV (32–36 wks gestation, seasonal)",
"AVOID live vaccines: MMR, Varicella, LAIV, Shingrix live (old Zostavax). RZV (Shingrix) is inactivated — defer until postpartum unless high risk."],
["Severely immunocompromised\n(HIV CD4 <200, transplant, biologics)",
"Annual flu (IIV — NOT LAIV), COVID-19, Pneumococcal, HepA, HepB, Shingrix (2 doses), Hib (if HSCT)",
"AVOID all live vaccines (MMR, VAR, LAIV, yellow fever). Shingrix (RZV) is safe and recommended. Time vaccines before starting immunosuppression when possible."],
["HIV-positive (CD4 ≥200)",
"All routine + Pneumococcal, HepA, HepB, Shingrix, MenACWY (if risk factor)",
"Live vaccines generally OK if CD4 ≥200 and not on ART-suppressed-only caveat; check current guidance. MMR, VAR may be given."],
["Asplenic / Complement deficiency",
"PCV20 or PCV21 + PPSV23 (2–5 yrs apart), MenACWY + MenB, Hib",
"If splenectomy elective: vaccinate ≥2 wks before surgery. Post-op: vaccinate as soon as stable (min 2 wks post-op). Booster MenACWY every 5 yrs."],
["Healthcare Workers (HCW)",
"MMR (2 doses confirmed), Varicella (2 doses confirmed), HepB (3 doses + titre check), Annual flu (IIV preferred), Tdap",
"Serologic confirmation of immunity recommended for MMR and HepB. Tdap at hire if not recent. Annual training on VPD protocols."],
["Solid organ / HSCT recipients",
"Annual flu (high-dose preferred), COVID-19, Pneumococcal, HepA, HepB, Shingrix",
"Wait 3–6 months post-transplant before re-vaccinating. Re-vaccinate HSCT as if never vaccinated. Avoid live vaccines while on immunosuppression."],
],
[32*mm, 70*mm, 78*mm]
),
]))
story.append(Spacer(1, 4*mm))
# ════════════════════════════════════════════════════════════════════════════
# SECTION 5 — CATCH-UP & CONTRAINDICATIONS (side by side)
# ════════════════════════════════════════════════════════════════════════════
story.append(section_header("5. Catch-Up Reference & Key Contraindications"))
story.append(Spacer(1, 2*mm))
catchup_data = [
[Paragraph("<b><font color='#1A3A5C'>Catch-Up Vaccination</font></b>", H3),
Paragraph("<b><font color='#1A3A5C'>Key Contraindications</font></b>", H3)],
[
make_table(
["Missed Vaccine", "Action"],
[
["Tdap (never received)", "1 dose now; then Td/Tdap booster every 10 yrs"],
["Hepatitis B", "3-dose series: 0, 1, 6 months. Accelerated: 0, 1, 2 mo + booster at 12 mo. Check anti-HBs titre after."],
["MMR (born ≥1957)", "1 dose; 2 doses if high-risk (28 days apart)"],
["Varicella (born ≥1980)", "2 doses, 4–8 weeks apart"],
["Shingrix (age ≥50)", "2 doses, 2–6 months apart. 2–12 mo if immunocompromised."],
["HPV (age ≤26, never started)", "Start series. 3 doses if ≥15 yrs at first dose."],
["Pneumococcal (age ≥65, none)", "PCV20 or PCV21 × 1. Or PCV15 + PPSV23 ≥1 yr later."],
],
[35*mm, 50*mm]
),
make_table(
["Contraindication", "Vaccines Affected"],
[
["Pregnancy", "MMR, Varicella, LAIV (intranasal flu), old Zostavax"],
["Severe immunosuppression", "All live vaccines (MMR, VAR, LAIV, yellow fever, oral typhoid)"],
["Anaphylaxis to eggs (severe)", "Avoid egg-based IIV3; use RIV3 (Flublok) or ccIIV3 (Flucelvax)"],
["Anaphylaxis to prior dose or vaccine component", "Contraindicated; assess in allergy/immunology if needed"],
["Active moderate-severe illness", "Defer all vaccines until recovered (mild illness is NOT a contraindication)"],
["Guillain-Barré within 6 wks of prior flu vaccine", "Relative CI; weigh risk vs. benefit"],
["Immunocompromised (live vaccine)", "Shingrix (RZV) is inactivated — SAFE. Old live Zostavax is contraindicated."],
],
[35*mm, 50*mm]
),
]
]
catchup_outer = Table(catchup_data, colWidths=[90*mm, 90*mm])
catchup_outer.setStyle(TableStyle([
("VALIGN", (0,0), (-1,-1), "TOP"),
("LEFTPADDING", (0,0), (-1,-1), 0),
("RIGHTPADDING", (0,0), (-1,-1), 4),
("TOPPADDING", (0,0), (-1,-1), 0),
("BOTTOMPADDING", (0,0), (-1,-1), 0),
]))
story.append(catchup_outer)
story.append(Spacer(1, 5*mm))
# ── FOOTER ──────────────────────────────────────────────────────────────────
story.append(HRFlowable(width="100%", thickness=0.5, color=TEAL))
story.append(Spacer(1, 2*mm))
story.append(Paragraph(
"Source: ACIP Recommended Immunization Schedule for Adults ≥19 yrs — United States, 2025 (MMWR 2025; PMID 39820474) | "
"Addendum: June 25, 2025 (RSV ages 50–59 with risk factors; MenABCWY Penmenvy). | "
"Always verify at cdc.gov/vaccines for the most current schedule. | "
f"Generated {date.today().strftime('%B %d, %Y')}",
FOOTER
))
# ── BUILD ────────────────────────────────────────────────────────────────────
doc.build(story)
print(f"PDF created: {OUTPUT}")
python /tmp/workspace/adult-vaccination/generate_pdf.py 2>&1
ls -lh /tmp/workspace/adult-vaccination/
Adult Vaccination Schedule
PDF Document · PDF
In india
India adult vaccination schedule 2025 IAP ACVIP recommended vaccines adults
"adult immunization" AND India
IAP ACVIP adult immunization schedule India 2024 2025 recommended vaccines site:acvip.org OR site:iapindia.org
PMID: 40553573
https://acvip.org/professional/columns/pcvs-vaccines
~/adult-vaccination/generate_india_pdf.py
from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.units import mm
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.platypus import (
SimpleDocTemplate, Table, TableStyle, Paragraph, Spacer,
HRFlowable, KeepTogether
)
from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_RIGHT
from datetime import date
OUTPUT = "/tmp/workspace/adult-vaccination/India_Adult_Vaccination_Schedule.pdf"
# ── Colours (warm saffron/green palette for India) ───────────────────────────
SAFFRON = colors.HexColor("#FF6600")
DKGREEN = colors.HexColor("#006600")
LTGREEN = colors.HexColor("#D5F5E3")
LTSAFF = colors.HexColor("#FEF0E0")
LTBLUE = colors.HexColor("#EAF4FB")
LTYELL = colors.HexColor("#FEF9E7")
LTGRAY = colors.HexColor("#F4F4F4")
NAVY = colors.HexColor("#1A3A5C")
WHITE = colors.white
DARKGRAY= colors.HexColor("#444444")
MIDGRAY = colors.HexColor("#888888")
LTRED = colors.HexColor("#FADBD8")
doc = SimpleDocTemplate(
OUTPUT,
pagesize=A4,
leftMargin=14*mm, rightMargin=14*mm,
topMargin=16*mm, bottomMargin=16*mm,
title="India Adult Vaccination Reference",
author="Orris Clinical Reference",
subject="IAP/ACVIP Recommended Adult Immunization Schedule - India",
)
styles = getSampleStyleSheet()
H2 = ParagraphStyle("H2", fontName="Helvetica-Bold", fontSize=10, textColor=WHITE,
alignment=TA_LEFT, spaceBefore=2, spaceAfter=2)
H2C = ParagraphStyle("H2C", fontName="Helvetica-Bold", fontSize=18, textColor=WHITE,
alignment=TA_CENTER)
SUB = ParagraphStyle("SUB", fontName="Helvetica", fontSize=8,
textColor=colors.HexColor("#FFE0BB"), alignment=TA_RIGHT, leading=11)
H3 = ParagraphStyle("H3", fontName="Helvetica-Bold", fontSize=9, textColor=NAVY,
spaceBefore=4, spaceAfter=2)
BODY= ParagraphStyle("BODY", fontName="Helvetica", fontSize=7.5,
textColor=DARKGRAY, leading=11)
BOLDS= ParagraphStyle("BOLDS", fontName="Helvetica-Bold", fontSize=7.5,
textColor=NAVY, leading=10)
SMALL= ParagraphStyle("SMALL", fontName="Helvetica", fontSize=7,
textColor=MIDGRAY, leading=9)
FOOTER= ParagraphStyle("FOOTER", fontName="Helvetica-Oblique", fontSize=6.5,
textColor=MIDGRAY, alignment=TA_CENTER)
WARN = ParagraphStyle("WARN", fontName="Helvetica-BoldOblique", fontSize=7.5,
textColor=colors.HexColor("#8B0000"), leading=10)
COL_W = 182*mm # full-width inner
def sec_hdr(text, bg=NAVY):
t = Table([[Paragraph(text, H2)]], colWidths=[COL_W])
t.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), bg),
("LEFTPADDING", (0,0), (-1,-1), 6),
("TOPPADDING", (0,0), (-1,-1), 3),
("BOTTOMPADDING", (0,0), (-1,-1), 3),
]))
return t
def make_table(headers, rows, col_widths, alt_colors=None):
data = [[Paragraph(f"<b>{h}</b>", BOLDS) for h in headers]]
for row in rows:
data.append([Paragraph(str(c), BODY) for c in row])
style = [
("BACKGROUND", (0,0), (-1,0), NAVY),
("TEXTCOLOR", (0,0), (-1,0), WHITE),
("FONTNAME", (0,0), (-1,0), "Helvetica-Bold"),
("FONTSIZE", (0,0), (-1,0), 7.5),
("ROWBACKGROUNDS", (0,1), (-1,-1), [WHITE, LTGRAY]),
("GRID", (0,0), (-1,-1), 0.3, colors.HexColor("#CCCCCC")),
("VALIGN", (0,0), (-1,-1), "TOP"),
("LEFTPADDING",(0,0), (-1,-1), 4),
("RIGHTPADDING",(0,0), (-1,-1), 4),
("TOPPADDING", (0,0), (-1,-1), 3),
("BOTTOMPADDING",(0,0), (-1,-1), 3),
]
if alt_colors:
for r, c in alt_colors:
style.append(("BACKGROUND", (0,r), (-1,r), c))
t = Table(data, colWidths=col_widths, repeatRows=1)
t.setStyle(TableStyle(style))
return t
story = []
# ── TITLE BANNER ─────────────────────────────────────────────────────────────
banner = Table([[
Paragraph("ADULT VACCINATION SCHEDULE — INDIA", H2C),
Paragraph(
f"IAP / ACVIP Recommendations<br/>Clinic Reference • {date.today().strftime('%B %d, %Y')}",
SUB)
]], colWidths=[120*mm, 62*mm])
banner.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), SAFFRON),
("LEFTPADDING", (0,0), (-1,-1), 6),
("RIGHTPADDING", (0,0), (-1,-1), 6),
("TOPPADDING", (0,0), (-1,-1), 8),
("BOTTOMPADDING",(0,0), (-1,-1), 8),
("VALIGN", (0,0), (-1,-1), "MIDDLE"),
]))
story.append(banner)
story.append(Spacer(1, 2*mm))
# ── IMPORTANT NOTE ───────────────────────────────────────────────────────────
note = Table([[
Paragraph(
"<b>Important:</b> India does NOT yet have a national government adult immunization programme. "
"The schedules below are from the <b>IAP/ACVIP (Indian Academy of Pediatrics / Advisory Committee on Vaccines and Immunization Practices)</b> "
"and expert body recommendations for use in private clinical practice. "
"Adult vaccination uptake in India remains very low (<2%). "
"Always check the latest ACVIP guidelines at acvip.org.",
BODY)
]], colWidths=[COL_W])
note.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), LTSAFF),
("BOX", (0,0), (-1,-1), 0.8, SAFFRON),
("LEFTPADDING", (0,0), (-1,-1), 6),
("RIGHTPADDING", (0,0), (-1,-1), 6),
("TOPPADDING", (0,0), (-1,-1), 4),
("BOTTOMPADDING",(0,0), (-1,-1), 4),
]))
story.append(note)
story.append(Spacer(1, 3*mm))
# ════════════════════════════════════════════════════════════════════════════
# SECTION 1 — VACCINES FOR ALL ADULTS (ROUTINE)
# ════════════════════════════════════════════════════════════════════════════
story.append(sec_hdr("1. Vaccines Recommended for ALL Adults", DKGREEN))
story.append(Spacer(1, 2*mm))
routine_rows = [
["Influenza\n(IIV – inactivated)",
"1 dose annually",
"All adults, especially HCW, elderly, pregnant, immunocompromised, chronic disease. "
"Best time: <b>pre-monsoon (May–June)</b> — use Southern Hemisphere strain (WHO South Asia zone). "
"In North India: additional winter peak (Oct–Dec). "
"IIV preferred over LAIV in adults ≥50 or immunocompromised."],
["Tetanus / Td / Tdap",
"Primary series (if never vaccinated): 3 doses at 0, 4 wks, 6 months. "
"Booster every 10 years thereafter.",
"Tdap preferred over Td for at least one dose in the series (whooping cough protection). "
"Tdap mandatory every pregnancy at 27–36 weeks. "
"Td for wound management (if last dose >5 years ago)."],
["COVID-19",
"As per current MoHFW/NTAGI guidance",
"Follow National Technical Advisory Group on Immunisation (NTAGI) schedule. "
"Updated boosters recommended for elderly and immunocompromised as per current national guidance."],
]
story.append(make_table(
["Vaccine", "Dose Schedule", "India-Specific Notes"],
routine_rows,
[32*mm, 50*mm, 100*mm]
))
story.append(Spacer(1, 4*mm))
# ════════════════════════════════════════════════════════════════════════════
# SECTION 2 — RECOMMENDED VACCINES (PRIVATE PRACTICE / IAP GUIDANCE)
# ════════════════════════════════════════════════════════════════════════════
story.append(sec_hdr("2. Recommended Vaccines in Private Practice (IAP/ACVIP)"))
story.append(Spacer(1, 2*mm))
iap_rows = [
["Hepatitis B\n(HepB)",
"3 doses: 0, 1, 6 months\nHeplisav-B: 2 doses (0, 1 month)",
"All unvaccinated adults. Priority for HCW, dialysis patients, sex workers, household contacts of HBsAg+.\n"
"Check anti-HBs 4–8 wks after series; if <10 mIU/mL, revaccinate.\n"
"Dialysis patients: double dose (40 mcg) series.\n"
"Brands available in India: Engerix-B, Recombivax, Shanvac-B (Shantha), Revac-B+, Genevac-B."],
["Hepatitis A\n(HepA)",
"2 doses: 0, 6–12 months\n(Havrix or Avaxim)",
"Adults without prior infection or vaccination. High priority: chronic liver disease, travel to rural/endemic areas, food handlers, MSM, drug users.\n"
"Twinrix (HepA+B combo): 3 doses at 0, 1, 6 months.\n"
"Most Indian adults may have natural immunity from childhood exposure; testing anti-HAV IgG before vaccination is cost-effective."],
["Typhoid\n(TCV or Vi-PS)",
"Typbar-TCV (conjugate): 1 dose\nVI polysaccharide (Typherix, Typhim Vi): 1 dose; booster every 3 years",
"Travel to endemic areas, food handlers, sanitation workers, military personnel.\n"
"TCV (conjugate) preferred — longer duration. Typhibev approved for single dose age >6 months to 45 years (no booster data yet).\n"
"India is high-burden; typhoid vaccine is highly relevant here."],
["MMR\n(Measles/Mumps/Rubella)",
"2 doses (4 weeks apart) if unvaccinated or uncertain history",
"Adults born after 1960 who lack documented vaccination or serologic immunity.\n"
"Priority: HCW, women of childbearing age (pre-pregnancy), students, travelers.\n"
"Do NOT give to pregnant women — defer to postpartum."],
["Varicella\n(Chickenpox)",
"2 doses, 4–8 weeks apart\n(if no prior disease or vaccination)",
"Adults with no history of chickenpox (approximately 5–10% of Indian adults remain susceptible despite widespread natural exposure).\n"
"Priority: HCW, family members of immunocompromised patients, women pre-pregnancy.\n"
"Serology testing before vaccination is cost-effective in India."],
["HPV\n(Gardasil 9 / Cervavac)",
"Ages 9–26 yrs: 2 doses (0, 6 months) if started <15 yrs;\n3 doses if ≥15 yrs.\nAges 27–45: Shared decision.",
"Cervavac (quadrivalent, made by Serum Institute) — India's first indigenous HPV vaccine, available since 2023 at lower cost (~₹2,000/dose vs ₹3,500+ for Gardasil 9).\n"
"Govt of India launched free HPV vaccination for girls 9–14 yrs under national programme (2024).\n"
"Indicated for women preventing cervical cancer; also for boys/men to prevent genital warts and HPV-related cancers."],
["Pneumococcal\n(PCV13/PPSV23)",
"PCV13 × 1 dose, then PPSV23 ≥8 weeks later (for high-risk adults);\nPPSV23 × 1 dose for age ≥65;\nRevaccinate with PPSV23 after 5 yrs if immunocompromised",
"High-risk adults of any age: chronic heart/lung/liver/renal disease, DM, asplenia, immunocompromised, cochlear implants, CSF leak.\n"
"Routine for all adults ≥65 yrs.\n"
"PCV20/PCV21 (US standard) not yet widely available in India; PCV13 + PPSV23 is the standard combination here.\n"
"Brands: Prevenar 13 (PCV13), Pneumovax 23 (PPSV23)."],
["Meningococcal\n(MenACWY / MenB)",
"MenACWY: 1 dose (Menactra/Menveo); booster every 5 yrs if ongoing risk.\nMenB: 2 doses",
"High-risk: asplenia, complement deficiency, immunosuppression, microbiologists.\n"
"Travel: Hajj pilgrims (Saudi Arabia requires MenACWY), travel to 'meningitis belt' of sub-Saharan Africa.\n"
"Menevo (MenACWY) approved in India for age 2–55 years.\n"
"Not part of UIP; available in private clinics."],
["Zoster\n(Shingrix / RZV)",
"2 doses, 2–6 months apart\n(for adults ≥50 yrs)",
"Recommended for all adults ≥50 yrs including those with prior shingles or prior live Zostavax.\n"
"Especially important: immunocompromised patients ≥18 yrs (e.g. bone marrow transplant, HIV, chemotherapy).\n"
"Shingrix (RZV, recombinant adjuvanted) — available in India; old Zostavax (live) is discontinued.\n"
"Shingles burden is significant in India but vaccine uptake is negligible."],
["Rabies (PrEP)\n(Pre-exposure)",
"3 doses: Days 0, 7, 21 or 28\n(IM or ID)\nBooster: every 2 years (or check titres)",
"High-risk only: veterinarians, lab workers handling virus, forest workers, travellers to high-risk areas.\n"
"India accounts for ~36% of global rabies deaths.\n"
"Post-exposure: 4 or 5-dose IM series (days 0, 3, 7, 14 ± 28) + RIG or monoclonal Ab (Rabieshield/Twinrab) for Category III bites.\n"
"Brands: Indirab, Rabipur, Verorab."],
["Japanese Encephalitis\n(JE)",
"2 doses (SA 14-14-2 live or killed): 0 and 28 days",
"Travel to/residence in endemic areas of India (UP, Bihar, Assam, West Bengal, Karnataka, Goa, Tamil Nadu).\n"
"Government of India provides JE vaccination free in endemic districts.\n"
"Risk highest July–September in most regions."],
["Cholera\n(Oral — Shanchol/mORC-Vax)",
"2 doses, 2 weeks apart\nBooster: every 2 years",
"Travel to cholera-endemic or outbreak areas. Humanitarian workers.\n"
"Shanchol (Shantha Biotech) available in India; WHO prequalified.\n"
"Not part of routine UIP for adults."],
]
story.append(make_table(
["Vaccine", "Dose Schedule", "India-Specific Notes & Brands"],
iap_rows,
[30*mm, 48*mm, 104*mm],
alt_colors=[
(1, LTBLUE),(2, LTBLUE),
(3, LTYELL),(4, LTYELL),
(5, LTGREEN),(6, LTGREEN),
(7, LTBLUE),(8, LTBLUE),
(9, LTYELL),(10, LTYELL),
(11, LTGREEN),(12, LTGREEN),
]
))
story.append(Spacer(1, 4*mm))
# ════════════════════════════════════════════════════════════════════════════
# SECTION 3 — SPECIAL POPULATIONS
# ════════════════════════════════════════════════════════════════════════════
story.append(sec_hdr("3. Special Populations", DKGREEN))
story.append(Spacer(1, 2*mm))
pop_rows = [
["Pregnant Women",
"Tdap (27–36 wks, every pregnancy)\nInfluenza (any trimester)\nCOVID-19 (as per current guidance)",
"AVOID: MMR, Varicella, LAIV, Shingrix live.\n"
"HepA and HepB may be given if clinically indicated (inactivated/recombinant — safe in pregnancy).\n"
"Typhoid (injectable Vi-PS only — live oral typhoid is contraindicated)."],
["Healthcare Workers\n(HCW)",
"MMR ×2 doses (if no documented immunity)\nVaricella ×2 doses\nHepB ×3 doses + titre check\nAnnual Influenza (IIV)\nTdap ×1",
"Serology confirmation recommended for HepB (anti-HBs ≥10 mIU/mL) and MMR.\n"
"Particularly important given high occupational TB and HBV exposure in Indian hospitals.\n"
"COVID-19 vaccination mandatory in many Indian hospital settings."],
["Immunocompromised\n(HIV, transplant, chemo, biologics)",
"IIV (annual), COVID-19, HepA, HepB, Pneumococcal (PCV13 + PPSV23), Shingrix",
"AVOID all live vaccines: MMR, Varicella, LAIV, JE live, oral typhoid.\n"
"Shingrix (RZV) is inactivated — SAFE and strongly recommended ≥18 yrs.\n"
"For HIV: vaccinate when CD4 count ≥200 cells/μL if possible. Live vaccines (MMR, VAR) generally safe at CD4 ≥200."],
["Asplenic Adults\n(surgical or functional)",
"Pneumococcal (PCV13 + PPSV23), MenACWY, Hib ×1 dose",
"Vaccinate ≥2 weeks before elective splenectomy.\n"
"Post-op: vaccinate ≥2 weeks after surgery (once haemodynamically stable).\n"
"MenACWY booster every 5 years. Hib: 1 dose.\n"
"Lifelong penicillin prophylaxis also standard in India for asplenic patients."],
["Elderly (≥60 yrs)",
"Annual Influenza (IIV)\nPneumococcal (PPSV23 ±PCV13)\nShingrix ×2 doses\nTd/Tdap booster (if due)\nCOVID-19 updated doses",
"This population has the highest VPD burden in India but the lowest vaccine uptake.\n"
"India lacks a national elderly vaccination programme — all at clinician's discretion.\n"
"Shingles incidence increases sharply after age 60; Shingrix is highly cost-effective."],
["Hajj / Umrah Pilgrims",
"MenACWY: mandatory (Saudi visa requirement — must be within 3 years for adults)\nInfluenza: strongly recommended",
"Saudi Arabia requires MenACWY vaccination certificate ≤3 years before travel.\n"
"Meningococcal outbreaks have been repeatedly linked to Hajj.\n"
"Also recommended: Hepatitis A, Typhoid, COVID-19 (check current Saudi entry requirements)."],
["Travellers",
"Destination-dependent (see column 3)",
"HepA + HepB (combo Twinrix): most destinations.\n"
"Typhoid: South Asia, SE Asia, Africa, Central/South America.\n"
"Yellow Fever: Sub-Saharan Africa, South America — requires certified vaccination center.\n"
"JE: rural Asia.\n"
"Rabies PrEP: long stay in endemic areas.\n"
"Meningococcal: Hajj/Umrah, sub-Saharan Africa meningitis belt."],
["Diabetics",
"Annual Influenza, Pneumococcal, HepB (if unvaccinated, high risk)",
"Diabetics have higher risk of invasive pneumococcal disease and HBV (via shared glucose monitoring equipment).\n"
"CDC/IAP recommend HepB for unvaccinated adults with DM."],
]
story.append(make_table(
["Population", "Key Vaccines", "Notes"],
pop_rows,
[32*mm, 58*mm, 92*mm]
))
story.append(Spacer(1, 4*mm))
# ════════════════════════════════════════════════════════════════════════════
# SECTION 4 — INDIA CONTEXT & KEY DIFFERENCES FROM US/CDC SCHEDULE
# ════════════════════════════════════════════════════════════════════════════
story.append(sec_hdr("4. India-Specific Context & Key Differences from US/CDC Schedule"))
story.append(Spacer(1, 2*mm))
diff_rows = [
["No national adult immunisation programme",
"Unlike the US (CDC/ACIP), India has no government-backed universal adult vaccination schedule. "
"All adult vaccination is driven by private practice, IAP/ACVIP recommendations, and patient/clinician initiative. "
"Adult vaccination coverage <2% nationally (JAPI 2025, PMID 40553573)."],
["Typhoid is highly relevant",
"Typhoid is endemic across India. The TCV (Typbar-TCV by Bharat Biotech) is the preferred vaccine. "
"Food handlers, travellers within India, and immunocompromised adults should be prioritised."],
["Rabies is a major public health issue",
"India accounts for ~36% of global rabies deaths. Post-exposure prophylaxis (PEP) with 4–5 dose IM schedule plus "
"monoclonal antibody (Rabieshield or Twinrab, preferred) or RIG is the standard for Category III exposures. "
"Pre-exposure prophylaxis (PrEP) for high-risk adults."],
["HPV: Indigenous Cervavac available",
"Cervavac (quadrivalent HPV, Serum Institute of India) is now available at lower cost than imported Gardasil 9. "
"Government launched free HPV vaccination for girls 9–14 yrs in 2024. "
"Adults up to age 45 may benefit from shared decision-making discussion."],
["JE is regionally endemic",
"Japanese Encephalitis is endemic in UP, Bihar, Assam, West Bengal, Karnataka, TN, Goa. "
"Government of India provides free JE vaccine in endemic districts. "
"Adults newly migrating to or travelling in endemic areas should be vaccinated."],
["Influenza season differs",
"India has dual influenza peaks: monsoon (June–September nationwide) and winter (October–December in North India/Tamil Nadu Dec–Feb). "
"WHO classifies India under 'South Asia' zone, aligned with Southern Hemisphere strains. "
"Vaccinate pre-monsoon (May–June) with the latest available SH strain formulation."],
["HepA — natural immunity assessment",
"Most Indian adults born before ~1990 likely have natural anti-HAV immunity from childhood exposure (high endemicity). "
"Testing anti-HAV IgG total antibody before vaccinating is cost-effective and may avoid unnecessary vaccination."],
["Shingles (Zoster) — severely neglected",
"Shingrix is available in India but rarely used. No public awareness programme exists. "
"Estimated millions of shingles cases annually in India. Clinicians should proactively offer to adults ≥50 yrs."],
["MenACWY: Hajj requirement",
"Meningococcal ACWY vaccination is mandatory for Hajj and Umrah pilgrims (Saudi visa requirement). "
"Menevo (available in India, approved age 2–55 yrs) or Menveo may be used."],
]
story.append(make_table(
["Topic", "India Context"],
diff_rows,
[45*mm, 137*mm]
))
story.append(Spacer(1, 4*mm))
# ════════════════════════════════════════════════════════════════════════════
# SECTION 5 — CONTRAINDICATIONS
# ════════════════════════════════════════════════════════════════════════════
story.append(sec_hdr("5. Key Contraindications", DKGREEN))
story.append(Spacer(1, 2*mm))
ci_rows = [
["Pregnancy", "MMR, Varicella, LAIV (intranasal flu), live typhoid (Ty21a oral), Yellow Fever, old live Zostavax"],
["Severe immunosuppression", "All live vaccines: MMR, Varicella, LAIV, JE live (SA14-14-2), Yellow Fever, oral typhoid, old Zostavax.\nShingrix (RZV) is INACTIVATED — safe and recommended."],
["Anaphylaxis to eggs (severe)", "Yellow Fever vaccine (absolutely contraindicated). Egg-based influenza — use cell-culture (Flucelvax) or recombinant (Flublok) if available."],
["Anaphylaxis to prior dose or component", "Absolute CI; refer to allergist/immunologist."],
["Active moderate-severe febrile illness", "Defer all vaccines until recovery. Minor illness (e.g. mild cold) is NOT a contraindication."],
["Immunocompromised (relative CI for live vaccines)", "Assess CD4 count in HIV (OK if ≥200); avoid MMR/VAR with CD4 <200 or if on immunosuppressants."],
["Guillain-Barré syndrome (within 6 wks of prior flu vaccine)", "Relative contraindication for influenza vaccine; weigh risk-benefit."],
]
story.append(make_table(
["Contraindication", "Vaccines Affected / Notes"],
ci_rows,
[45*mm, 137*mm]
))
story.append(Spacer(1, 5*mm))
# ── FOOTER ──────────────────────────────────────────────────────────────────
story.append(HRFlowable(width="100%", thickness=0.5, color=SAFFRON))
story.append(Spacer(1, 2*mm))
story.append(Paragraph(
"Sources: IAP/ACVIP Recommendations (acvip.org) | JAPI 2025: Establishing Adult Vaccination Clinics in India (PMID 40553573) | "
"Indian J Med Res 2024: Adult vaccination in India (PMID 39632644) | MoHFW/NTAGI guidelines | "
"Always verify latest schedule at acvip.org and mohfw.gov.in | "
f"Generated {date.today().strftime('%B %d, %Y')}",
FOOTER
))
doc.build(story)
print(f"PDF created: {OUTPUT}")