Chemotherapy drugs used in ent head and neck and their mechanism of action
"head and neck squamous cell carcinoma" AND chemotherapy
1,2,3,4
| Drug class | Key mechanism | Cell cycle specificity | Dose-limiting toxicity | Typical H&N role |
|---|---|---|---|---|
| Cisplatin | DNA cross-linking (guanine adducts) | Non-specific, S-phase sensitive | Nephro/ototoxicity | Backbone of chemoRT/induction |
| Carboplatin | Same as cisplatin, less reactive | Non-specific | Myelosuppression | Substitute when cisplatin contraindicated |
| 5-FU | Thymidylate synthetase inhibition | S-phase specific | Mucositis, myelosuppression | Paired with cisplatin (PF) |
| Methotrexate | DHFR inhibition | S-phase specific | Myelosuppression, hepatotoxicity, mucositis | Palliative/recurrent, metronomic |
| Paclitaxel/Docetaxel | Microtubule stabilization | G2/M arrest | Neuropathy, neutropenia | Added to PF (TPF induction) |
| Vincristine/Vinblastine | Microtubule assembly inhibition | M-phase arrest | Neurotoxicity (vincristine), myelosuppression (vinblastine) | Sarcomas, limited use in SCC |
Create a comparison table of H&N chemotherapy regimens (PF vs TPF vs concurrent)
"induction chemotherapy" AND "head and neck" AND TPF
| Feature | PF (Induction) | TPF (Induction) | Concurrent Chemoradiotherapy |
|---|---|---|---|
| Drugs | Cisplatin + 5-Fluorouracil | Docetaxel (or Paclitaxel) + Cisplatin + 5-Fluorouracil | Cisplatin (high-dose q3wk or weekly low-dose) or Carboplatin, given alongside radiotherapy |
| Typical dosing | Cisplatin 100 mg/m² day 1; 5-FU continuous infusion (~1000 mg/m²/day) days 1-4/5, repeated q3wk for 2-3 cycles | Docetaxel 75 mg/m² day 1 + Cisplatin 75-100 mg/m² day 1 + 5-FU continuous infusion, q3wk x 3 cycles (TAX 324 / PARADIGM / GORTEC dosing) | Cisplatin 100 mg/m² every 21 days x 3 cycles (standard high-dose) OR weekly cisplatin/carboplatin at lower doses, concurrent with daily RT over 6-7 weeks |
| Mechanism basis | 5-FU blocks thymidylate synthetase (S-phase); cisplatin forms DNA cross-links (non-phase-specific) | Adds taxane (microtubule stabilization, G2/M arrest) on top of PF's mechanisms - broader cell-kill across cycle phases | Cisplatin's radiosensitizing effect (impairs DNA repair of radiation-induced damage) combined with direct cytotoxicity |
| Setting/goal | Induction before definitive RT/surgery - cytoreduction, organ preservation, assess chemosensitivity | Induction before definitive RT/surgery - more potent cytoreduction than PF | Definitive or adjuvant treatment - given as primary curative-intent therapy or post-op in high-risk patients |
| Response rates | ~59% overall response in GORTEC trial | ~80% overall response (GORTEC); superior to PF | N/A (not primarily measured by induction response; measured by locoregional control/survival) |
| Survival outcome | Inferior to TPF (5-yr survival ~42% in GORTEC-type trial) | Superior to PF: 5-yr survival ~52% vs 42% (GORTEC); improved larynx preservation rates | Confirmed absolute survival benefit of ~6.5% at 5 years when added to RT (meta-analysis level evidence) - current standard of care for locally advanced unresectable disease |
| Organ preservation | Effective, but less than TPF | Superior larynx/hypopharynx preservation vs PF, without increased rates of feeding tubes or tracheostomy | Also achieves organ preservation but as primary approach (avoids surgery altogether in many larynx/hypopharynx protocols) |
| Toxicity profile | Significant myelosuppression, mucositis, nausea; better tolerated than TPF | Higher toxicity than PF - increased hematologic toxicity (neutropenia, febrile neutropenia) and mucositis; requires growth factor support in many protocols | High-dose cisplatin: nephro/ototoxicity, severe mucositis, requires hydration; weekly regimens better tolerated but may sacrifice some efficacy |
| Sequencing | Followed by RT alone, surgery, or concurrent chemoRT | Followed by concurrent chemoRT (usually with weekly carboplatin) or RT alone | Given as single continuous course; no separate induction phase |
| Key supporting trials | GORTEC comparator arm; classic regimen predating taxane era | TAX 324, GORTEC, PARADIGM trials | Meta-analysis of chemoradiotherapy trials (MACH-NC); multiple RTOG trials establishing cisplatin-RT as standard |
| Current role | Largely superseded by TPF where induction is chosen; still used when taxane toxicity is a concern | Preferred induction regimen in fit patients considered for organ preservation, though with careful selection given added toxicity | Standard of care for locally advanced, unresectable HNSCC and as adjuvant treatment in high-risk post-surgical patients |
Make a comparison poster: organ preservation outcomes across all three regimens
A medical education poster titled "Organ Preservation Outcomes in Head & Neck Cancer Chemotherapy Regimens: PF vs TPF vs Concurrent Chemoradiotherapy". The poster should present a clear comparison table/infographic with these regimens as columns: "PF (Induction: Cisplatin + 5-FU)", "TPF (Induction: Docetaxel + Cisplatin + 5-FU)", and "Concurrent Chemoradiotherapy (Cisplatin + RT)". Include rows comparing: Larynx/Organ Preservation Rate (RTOG 91-11 trial: RT alone 70%, Induction PF 75%, Concurrent CRT 88%; EORTC/GORTEC TPF vs PF trial: PF 60%, TPF 80%), Overall Response Rate (PF 61%, TPF 83%), Locoregional Control Rate (Induction PF 61%, Concurrent CRT 78%), 5-Year Overall Survival (PF 42-54%, TPF 52%, Concurrent CRT 54-56%), and Severe/High-Grade Toxicity Rate (Concurrent CRT 82%, higher hematologic toxicity with TPF, moderate toxicity with PF). Include a bar chart visualizing the larynx preservation percentages across the three regimens. Add small citation notes referencing RTOG 91-11 trial and EORTC 24971/TAX 324 trial. Include a brief text box summarizing key takeaway: concurrent chemoradiotherapy achieves the highest organ preservation and locoregional control but with the highest toxicity, while TPF induction improves outcomes over PF induction alone. Use head and neck anatomy icons (larynx, throat) as visual accents.

| Metric | PF (Induction) | TPF (Induction) | Concurrent Chemoradiotherapy |
|---|---|---|---|
| Larynx/organ preservation rate | ~60-75% (RTOG 91-11 induction arm: 75%; EORTC/GORTEC comparator: lower than TPF) | ~80% (superior to PF in head-to-head trials) | ~88% (highest among the three - RTOG 91-11) |
| Overall response rate | ~61% | ~83% | N/A (not an induction endpoint) |
| Locoregional control | ~61% | Improved vs PF | ~78% (highest) |
| 5-year overall survival | ~42-54% | ~52% | ~54-56% (similar across RTOG 91-11 arms) |
| Severe/high-grade toxicity | Moderate (myelosuppression, mucositis) | Higher (added neutropenia/mucositis from taxane) | Highest (~82%) - trade-off for best organ preservation |
Create a quick reference PDF guide for H&N chemotherapy regimens
/tmp/skills/pdf
mkdir -p /tmp/workspace/hn-chemo-guide && ls -la /tmp/workspace/
~/hn-chemo-guide/build_guide.py
#!/usr/bin/env python3
"""
Quick Reference PDF Guide: Head & Neck Chemotherapy Regimens
"""
from reportlab.lib.pagesizes import letter
from reportlab.lib import colors
from reportlab.lib.units import inch
from reportlab.platypus import (
SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
PageBreak, HRFlowable, ListFlowable, ListItem
)
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.enums import TA_CENTER, TA_LEFT
OUT = "/tmp/workspace/hn-chemo-guide/HN_Chemotherapy_Quick_Reference_Guide.pdf"
doc = SimpleDocTemplate(
OUT, pagesize=letter,
topMargin=0.55*inch, bottomMargin=0.55*inch,
leftMargin=0.55*inch, rightMargin=0.55*inch,
title="H&N Chemotherapy Quick Reference Guide"
)
styles = getSampleStyleSheet()
title_style = ParagraphStyle('TitleC', parent=styles['Title'], fontSize=20, textColor=colors.HexColor('#0B3D66'), spaceAfter=4, alignment=TA_CENTER)
subtitle_style = ParagraphStyle('Subtitle', parent=styles['Normal'], fontSize=11, textColor=colors.HexColor('#555555'), alignment=TA_CENTER, spaceAfter=14)
h1 = ParagraphStyle('H1', parent=styles['Heading1'], fontSize=14, textColor=colors.white, backColor=colors.HexColor('#0B3D66'), spaceBefore=10, spaceAfter=8, leftIndent=6, borderPadding=(4,4,4,4))
h2 = ParagraphStyle('H2', parent=styles['Heading2'], fontSize=11.5, textColor=colors.HexColor('#0B3D66'), spaceBefore=8, spaceAfter=4)
body = ParagraphStyle('Body', parent=styles['Normal'], fontSize=9, leading=12)
small = ParagraphStyle('Small', parent=styles['Normal'], fontSize=8, leading=10, textColor=colors.HexColor('#444444'))
cite = ParagraphStyle('Cite', parent=styles['Normal'], fontSize=7.5, leading=9.5, textColor=colors.HexColor('#666666'), spaceBefore=4)
takeaway_style = ParagraphStyle('Takeaway', parent=styles['Normal'], fontSize=9, leading=12, textColor=colors.HexColor('#0B3D66'))
def section_header(text):
return Paragraph(text, h1)
def make_table(data, col_widths=None, header_bg='#0B3D66', font_size=8, align_first_left=True, zebra=True):
t = Table(data, colWidths=col_widths, repeatRows=1)
style_cmds = [
('BACKGROUND', (0,0), (-1,0), colors.HexColor(header_bg)),
('TEXTCOLOR', (0,0), (-1,0), colors.white),
('FONTNAME', (0,0), (-1,0), 'Helvetica-Bold'),
('FONTSIZE', (0,0), (-1,-1), font_size),
('GRID', (0,0), (-1,-1), 0.5, colors.HexColor('#BBBBBB')),
('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 align_first_left:
style_cmds.append(('ALIGN', (0,1), (0,-1), 'LEFT'))
style_cmds.append(('FONTNAME', (0,1), (0,-1), 'Helvetica-Bold'))
if zebra:
for i in range(1, len(data)):
if i % 2 == 0:
style_cmds.append(('BACKGROUND', (0,i), (-1,i), colors.HexColor('#F2F6FA')))
t.setStyle(TableStyle(style_cmds))
return t
story = []
# ===== TITLE =====
story.append(Paragraph("Head & Neck Chemotherapy", title_style))
story.append(Paragraph("Quick Reference Guide", title_style))
story.append(Paragraph("Drug Mechanisms | Regimen Comparison | Organ Preservation Outcomes | Toxicity", subtitle_style))
story.append(HRFlowable(width="100%", thickness=1, color=colors.HexColor('#0B3D66')))
story.append(Spacer(1, 10))
# ===== SECTION 1: DRUG CLASSES =====
story.append(section_header("1. Core Chemotherapy Drug Classes & Mechanisms"))
drug_data = [
["Class", "Key Agents", "Mechanism of Action", "Dose-Limiting Toxicity"],
["Platinum agents", "Cisplatin, Carboplatin",
"DNA cross-linking (guanine N7 adducts) -> blocks DNA/RNA/protein synthesis",
"Cisplatin: nephro-/ototoxicity. Carboplatin: myelosuppression"],
["Antimetabolites", "5-Fluorouracil (5-FU), Methotrexate",
"5-FU: inhibits thymidylate synthetase (S-phase). MTX: inhibits dihydrofolate reductase, blocks folate-dependent DNA/RNA synthesis",
"Mucositis, myelosuppression, hepatotoxicity (MTX)"],
["Taxanes", "Docetaxel, Paclitaxel",
"Stabilize microtubules against depolymerization -> G2/M mitotic arrest",
"Neutropenia, peripheral neuropathy, hypersensitivity"],
["Vinca alkaloids", "Vincristine, Vinblastine",
"Inhibit tubulin polymerization -> block microtubule assembly -> M-phase arrest",
"Vincristine: neurotoxicity. Vinblastine: myelosuppression"],
["Cytotoxic antibiotics", "Bleomycin, Mitomycin C",
"Bleomycin: DNA strand fragmentation. Mitomycin C: DNA cross-linking",
"Pulmonary fibrosis (bleomycin), myelosuppression"],
]
story.append(make_table(drug_data, col_widths=[1.1*inch, 1.3*inch, 3.0*inch, 1.7*inch]))
story.append(Spacer(1, 10))
# ===== SECTION 2: REGIMEN COMPARISON =====
story.append(section_header("2. Regimen Comparison: PF vs TPF vs Concurrent Chemoradiotherapy"))
regimen_data = [
["Feature", "PF (Induction)", "TPF (Induction)", "Concurrent Chemoradiotherapy"],
["Drugs", "Cisplatin + 5-FU", "Docetaxel + Cisplatin + 5-FU", "Cisplatin (high-dose q3wk or weekly) +/- Carboplatin, with RT"],
["Typical dosing", "Cisplatin 100 mg/m2 D1; 5-FU CI ~1000 mg/m2/d D1-4/5; q3wk x2-3 cycles",
"Docetaxel 75 mg/m2 D1 + Cisplatin 75-100 mg/m2 D1 + 5-FU CI; q3wk x3 cycles",
"Cisplatin 100 mg/m2 q21d x3 cycles OR weekly low-dose; concurrent with daily RT x6-7 wks"],
["Goal / setting", "Cytoreduction before RT/surgery; organ preservation",
"More potent cytoreduction than PF before definitive RT/surgery",
"Primary curative-intent or adjuvant treatment (definitive or post-op high-risk)"],
["Response rate", "~61%", "~83% (superior to PF)", "N/A (not an induction endpoint)"],
["Larynx / organ preservation", "~75% (RTOG 91-11 induction arm)", "~80% (superior to PF)", "~88% (highest; RTOG 91-11)"],
["Locoregional control", "~61%", "Improved vs PF", "~78% (highest)"],
["5-year overall survival", "~42-54%", "~52%", "~54-56%"],
["Severe/high-grade toxicity", "Moderate (myelosuppression, mucositis)", "Higher (added neutropenia/mucositis)", "Highest (~82%) - trade-off for best preservation"],
["Current role", "Superseded by TPF where induction chosen; used if taxane risk high",
"Preferred induction in fit patients selected for organ preservation",
"Standard of care for locally advanced unresectable HNSCC; adjuvant in high-risk post-op"],
]
story.append(make_table(regimen_data, col_widths=[1.3*inch, 1.7*inch, 1.9*inch, 2.2*inch], font_size=7.6))
story.append(Paragraph(
"Sources: GORTEC (TPF vs PF, laryngeal/hypopharyngeal), TAX 324/EORTC 24971, RTOG 91-11 (3-arm laryngeal preservation trial), "
"Cummings Otolaryngology Head and Neck Surgery; Scott-Brown's Otorhinolaryngology Head & Neck Surgery.", cite))
story.append(Spacer(1, 8))
story.append(PageBreak())
# ===== SECTION 3: ORGAN PRESERVATION DEEP DIVE (RTOG 91-11) =====
story.append(section_header("3. Organ Preservation Outcomes - RTOG 91-11 (3-Arm Laryngeal Trial)"))
rtog_data = [
["Arm", "Larynx Preservation", "Locoregional Control", "5-yr Overall Survival", "Severe Toxicity"],
["RT alone", "70%", "56%", "54-56%", "Lowest of the three"],
["Induction PF -> RT", "75%", "61%", "54-56%", "Moderate"],
["Concurrent Cisplatin + RT", "88% (highest)", "78% (highest)", "54-56% (similar across arms)", "82% (highest)"],
]
story.append(make_table(rtog_data, col_widths=[1.7*inch, 1.5*inch, 1.5*inch, 1.6*inch, 1.6*inch], font_size=8))
story.append(Spacer(1, 8))
story.append(section_header("4. TPF vs PF - Induction Head-to-Head (GORTEC / TAX 324)"))
tpf_pf_data = [
["Endpoint", "PF", "TPF"],
["Overall response rate", "59-61%", "80-83%"],
["Larynx preservation rate", "~57-60%", "~80%"],
["5-year overall survival", "42%", "52%"],
["Feeding tube / tracheostomy rate", "Baseline", "Not increased vs PF despite higher response"],
]
story.append(make_table(tpf_pf_data, col_widths=[2.5*inch, 2.4*inch, 2.4*inch], font_size=8.5))
story.append(Spacer(1, 10))
# ===== SECTION 5: KEY TAKEAWAYS =====
story.append(section_header("5. Key Clinical Takeaways"))
takeaways = [
"Concurrent cisplatin-chemoradiotherapy gives the highest larynx preservation (88%) and locoregional control (78%) of any single strategy, but carries the highest toxicity burden (82% severe acute/late effects) - RTOG 91-11.",
"5-year overall survival is similar across RT alone, induction PF, and concurrent CRT (~54-56%) - the real differentiator between strategies is organ preservation and toxicity, not survival.",
"When induction chemotherapy is chosen as the strategy, TPF is superior to PF: higher response rate (83% vs 61%), better larynx preservation (~80% vs ~60%), and improved 5-year survival (52% vs 42%) - without increasing feeding-tube or tracheostomy rates.",
"TPF carries greater hematologic toxicity (neutropenia, febrile neutropenia) and mucositis than PF - patient selection (performance status, renal/hearing function, ability to tolerate triplet chemo) is critical.",
"Sequential therapy (induction TPF followed by concurrent chemoRT) has not shown a clear survival advantage over concurrent chemoRT alone in randomized comparison (PARADIGM trial) - added toxicity without proven benefit in unselected patients.",
"Carboplatin is substituted for cisplatin when renal impairment, hearing loss, or poor tolerance is a concern, at the cost of somewhat reduced radiosensitizing potency.",
]
bullets = ListFlowable(
[ListItem(Paragraph(t, body), bulletColor=colors.HexColor('#0B3D66')) for t in takeaways],
bulletType='bullet', start='circle', leftIndent=14
)
story.append(bullets)
story.append(Spacer(1, 10))
# ===== SECTION 6: TOXICITY QUICK-CHECK =====
story.append(section_header("6. Toxicity Quick-Check by Agent"))
tox_data = [
["Agent", "Watch For", "Monitoring"],
["Cisplatin", "Nephrotoxicity, ototoxicity, peripheral neuropathy, severe nausea", "Creatinine/CrCl, audiology, Mg/electrolytes, aggressive hydration"],
["Carboplatin", "Myelosuppression (thrombocytopenia), hypersensitivity", "CBC, platelet count"],
["5-Fluorouracil", "Mucositis, diarrhea, myelosuppression, cardiotoxicity (rare)", "Oral exam, CBC, DPD deficiency screen if available"],
["Methotrexate", "Myelosuppression, hepatotoxicity, mucositis, renal toxicity (high dose)", "CBC, LFTs monthly, hydration/urine alkalinization for high dose"],
["Docetaxel/Paclitaxel", "Neutropenia, peripheral neuropathy, hypersensitivity, fluid retention (docetaxel)", "CBC with differential, neuro exam, premedication for hypersensitivity"],
]
story.append(make_table(tox_data, col_widths=[1.6*inch, 3.1*inch, 2.4*inch], font_size=8))
story.append(Spacer(1, 8))
story.append(HRFlowable(width="100%", thickness=0.7, color=colors.HexColor('#0B3D66')))
story.append(Paragraph(
"This guide is a teaching summary compiled from Scott-Brown's Otorhinolaryngology Head & Neck Surgery, "
"Cummings Otolaryngology Head and Neck Surgery, K J Lee's Essential Otolaryngology, and Goodman & Gilman's "
"Pharmacological Basis of Therapeutics, supplemented by RTOG 91-11, GORTEC, and TAX 324/EORTC 24971 trial data. "
"Not a substitute for institutional protocols or current oncology guidelines - verify dosing and eligibility "
"against up-to-date clinical trial/NCCN sources before clinical use.", cite))
doc.build(story)
print("PDF created at", OUT)
cd /tmp/workspace/hn-chemo-guide && python3 build_guide.py
cd /tmp/workspace/hn-chemo-guide && python3 -c " from pypdf import PdfReader r = PdfReader('HN_Chemotherapy_Quick_Reference_Guide.pdf') print('Pages:', len(r.pages)) for i,p in enumerate(r.pages): print(f'--- page {i+1} ---') print(p.extract_text()[:300]) " ls -la /tmp/workspace/hn-chemo-guide/
HN Chemotherapy Quick Reference Guide
PDF Document · PDF
Pdf is not created nicely
~/hn-chemo-guide/build_guide.py
#!/usr/bin/env python3
"""
Quick Reference PDF Guide: Head & Neck Chemotherapy Regimens
Redesigned for clean, consistent, non-overflowing layout.
"""
from reportlab.lib.pagesizes import letter
from reportlab.lib import colors
from reportlab.lib.units import inch
from reportlab.platypus import (
SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
PageBreak, HRFlowable, ListFlowable, ListItem, Frame, BaseDocTemplate,
PageTemplate
)
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.enums import TA_CENTER, TA_LEFT
OUT = "/tmp/workspace/hn-chemo-guide/HN_Chemotherapy_Quick_Reference_Guide.pdf"
PAGE_W, PAGE_H = letter
MARGIN = 0.6 * inch
USABLE_W = PAGE_W - 2 * MARGIN # ~7.3in
NAVY = colors.HexColor('#0B3D66')
NAVY_LIGHT = colors.HexColor('#E7EEF5')
ACCENT = colors.HexColor('#C0392B')
GREY = colors.HexColor('#555555')
ROW_ALT = colors.HexColor('#F4F7FA')
GRID_COLOR = colors.HexColor('#C9D3DC')
styles = getSampleStyleSheet()
title_style = ParagraphStyle('TitleC', parent=styles['Title'], fontName='Helvetica-Bold',
fontSize=22, textColor=NAVY, alignment=TA_CENTER, spaceAfter=2)
subtitle_style = ParagraphStyle('Subtitle', parent=styles['Normal'], fontName='Helvetica',
fontSize=10.5, textColor=GREY, alignment=TA_CENTER, spaceAfter=2)
banner_text_style = ParagraphStyle('BannerText', parent=styles['Normal'], fontName='Helvetica-Bold',
fontSize=12.5, textColor=colors.white, leading=15)
body = ParagraphStyle('Body', parent=styles['Normal'], fontName='Helvetica', fontSize=8.6, leading=11.2)
body_bold = ParagraphStyle('BodyBold', parent=body, fontName='Helvetica-Bold', textColor=NAVY)
head_cell = ParagraphStyle('HeadCell', parent=styles['Normal'], fontName='Helvetica-Bold',
fontSize=8.6, leading=11, textColor=colors.white)
cite = ParagraphStyle('Cite', parent=styles['Normal'], fontName='Helvetica-Oblique', fontSize=7.6,
leading=10, textColor=GREY, spaceBefore=6)
takeaway_body = ParagraphStyle('TakeawayBody', parent=body, fontSize=9, leading=12.5)
footer_style = ParagraphStyle('Footer', parent=styles['Normal'], fontSize=7.5, textColor=GREY)
def banner(text):
"""Full-width colored section banner using a 1x1 table (reliable background rendering)."""
p = Paragraph(text, banner_text_style)
t = Table([[p]], colWidths=[USABLE_W])
t.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), 5),
('BOTTOMPADDING', (0, 0), (-1, -1), 5),
]))
return t
def cell(text, style=body):
return Paragraph(text, style)
def make_table(rows, col_widths, header_style=head_cell, body_style=body,
first_col_bold=True, font_size=None):
"""Build a table where every cell is a Paragraph (reliable wrapping, no overflow)."""
assert abs(sum(col_widths) - USABLE_W) < 0.05 * inch, \
f"Column widths sum {sum(col_widths)/inch:.2f}in != usable width {USABLE_W/inch:.2f}in"
data = []
header_row = [Paragraph(h, header_style) for h in rows[0]]
data.append(header_row)
for r in rows[1:]:
row_cells = []
for idx, val in enumerate(r):
if idx == 0 and first_col_bold:
row_cells.append(Paragraph(val, body_bold))
else:
row_cells.append(Paragraph(val, body_style))
data.append(row_cells)
t = Table(data, colWidths=col_widths, repeatRows=1)
style_cmds = [
('BACKGROUND', (0, 0), (-1, 0), NAVY),
('GRID', (0, 0), (-1, -1), 0.6, GRID_COLOR),
('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
('LEFTPADDING', (0, 0), (-1, -1), 5),
('RIGHTPADDING', (0, 0), (-1, -1), 5),
('TOPPADDING', (0, 0), (-1, -1), 4),
('BOTTOMPADDING', (0, 0), (-1, -1), 4),
]
for i in range(1, len(data)):
if i % 2 == 0:
style_cmds.append(('BACKGROUND', (0, i), (-1, i), ROW_ALT))
t.setStyle(TableStyle(style_cmds))
return t
# ---------- Page decoration (header rule + footer with page number) ----------
def draw_page_decoration(canvas, doc_):
canvas.saveState()
# Footer rule
canvas.setStrokeColor(GRID_COLOR)
canvas.setLineWidth(0.5)
canvas.line(MARGIN, 0.5 * inch, PAGE_W - MARGIN, 0.5 * inch)
# Footer text
canvas.setFont('Helvetica', 7.5)
canvas.setFillColor(GREY)
canvas.drawString(MARGIN, 0.34 * inch, "H&N Chemotherapy Quick Reference Guide")
canvas.drawRightString(PAGE_W - MARGIN, 0.34 * inch, f"Page {doc_.page}")
canvas.restoreState()
doc = BaseDocTemplate(
OUT, pagesize=letter,
topMargin=MARGIN, bottomMargin=0.7 * inch,
leftMargin=MARGIN, rightMargin=MARGIN,
title="H&N Chemotherapy Quick Reference Guide"
)
frame = Frame(MARGIN, 0.7 * inch, USABLE_W, PAGE_H - MARGIN - 0.7 * inch, id='normal')
doc.addPageTemplates([PageTemplate(id='main', frames=[frame], onPage=draw_page_decoration)])
story = []
# ===== TITLE BLOCK =====
story.append(Spacer(1, 4))
story.append(Paragraph("Head & Neck Chemotherapy", title_style))
story.append(Paragraph("Quick Reference Guide", title_style))
story.append(Spacer(1, 4))
story.append(Paragraph(
"Drug Mechanisms | Regimen Comparison | "
"Organ Preservation Outcomes | Toxicity", subtitle_style))
story.append(Spacer(1, 6))
story.append(HRFlowable(width="100%", thickness=1.4, color=NAVY))
story.append(Spacer(1, 12))
# ===== SECTION 1: DRUG CLASSES =====
story.append(banner("1. Core Chemotherapy Drug Classes & Mechanisms"))
story.append(Spacer(1, 6))
drug_rows = [
["Class", "Key Agents", "Mechanism of Action", "Dose-Limiting Toxicity"],
["Platinum agents", "Cisplatin, Carboplatin",
"DNA cross-linking at guanine N7 → blocks DNA/RNA/protein synthesis",
"Cisplatin: nephro-/ototoxicity. Carboplatin: myelosuppression"],
["Antimetabolites", "5-Fluorouracil, Methotrexate",
"5-FU inhibits thymidylate synthetase (S-phase). Methotrexate inhibits dihydrofolate reductase, blocking folate-dependent DNA/RNA synthesis",
"Mucositis, myelosuppression, hepatotoxicity (MTX)"],
["Taxanes", "Docetaxel, Paclitaxel",
"Stabilize microtubules against depolymerization → G2/M mitotic arrest",
"Neutropenia, peripheral neuropathy, hypersensitivity"],
["Vinca alkaloids", "Vincristine, Vinblastine",
"Inhibit tubulin polymerization → block microtubule assembly → M-phase arrest",
"Vincristine: neurotoxicity. Vinblastine: myelosuppression"],
["Cytotoxic antibiotics", "Bleomycin, Mitomycin C",
"Bleomycin causes DNA strand fragmentation; Mitomycin C cross-links DNA",
"Pulmonary fibrosis (bleomycin), myelosuppression"],
]
story.append(make_table(drug_rows, col_widths=[1.1*inch, 1.35*inch, 3.15*inch, 1.7*inch]))
story.append(Spacer(1, 14))
# ===== SECTION 2: REGIMEN COMPARISON =====
story.append(banner("2. Regimen Comparison: PF vs TPF vs Concurrent Chemoradiotherapy"))
story.append(Spacer(1, 6))
regimen_rows = [
["Feature", "PF (Induction)", "TPF (Induction)", "Concurrent CRT"],
["Drugs", "Cisplatin + 5-FU",
"Docetaxel + Cisplatin + 5-FU",
"Cisplatin (or carboplatin) given with radiotherapy"],
["Typical dosing", "Cisplatin 100 mg/m² D1; 5-FU CI ~1000 mg/m²/day D1-4/5; q3wk x2-3 cycles",
"Docetaxel 75 mg/m² D1 + Cisplatin 75-100 mg/m² D1 + 5-FU CI; q3wk x3 cycles",
"Cisplatin 100 mg/m² q21d x3 cycles, or weekly low-dose, concurrent with daily RT x6-7 wks"],
["Goal / setting", "Cytoreduction before RT/surgery; organ preservation",
"More potent cytoreduction than PF before definitive RT/surgery",
"Primary curative-intent or adjuvant treatment"],
["Response rate", "~61%", "~83% (superior to PF)", "N/A (not an induction endpoint)"],
["Organ preservation", "~75% (RTOG 91-11 induction arm)", "~80% (superior to PF)", "~88% (highest)"],
["Locoregional control", "~61%", "Improved vs PF", "~78% (highest)"],
["5-yr overall survival", "~42-54%", "~52%", "~54-56%"],
["Severe toxicity", "Moderate", "Higher than PF", "Highest (~82%)"],
]
story.append(make_table(regimen_rows, col_widths=[1.3*inch, 1.9*inch, 1.95*inch, 2.15*inch], font_size=7.8))
story.append(Spacer(1, 6))
story.append(Paragraph(
"Sources: GORTEC (TPF vs PF), TAX 324 / EORTC 24971, RTOG 91-11; Cummings Otolaryngology Head and Neck "
"Surgery; Scott-Brown's Otorhinolaryngology Head & Neck Surgery.", cite))
story.append(PageBreak())
# ===== SECTION 3: RTOG 91-11 =====
story.append(banner("3. Organ Preservation Outcomes — RTOG 91-11 (3-Arm Laryngeal Trial)"))
story.append(Spacer(1, 6))
rtog_rows = [
["Arm", "Organ (Larynx) Preservation", "Locoregional Control", "5-yr Overall Survival", "Severe Toxicity"],
["RT alone", "70%", "56%", "54-56%", "Lowest of the three arms"],
["Induction PF → RT", "75%", "61%", "54-56%", "Moderate"],
["Concurrent Cisplatin + RT", "88% (highest)", "78% (highest)", "54-56% (similar across arms)", "82% (highest)"],
]
story.append(make_table(rtog_rows, col_widths=[1.55*inch, 1.55*inch, 1.4*inch, 1.5*inch, 1.3*inch], font_size=7.6))
story.append(Spacer(1, 4))
story.append(Paragraph(
"Key point: overall survival is statistically similar across all three arms — concurrent chemoradiotherapy "
"wins on organ preservation and locoregional control, not on survival, and this benefit comes at the cost of "
"the highest toxicity.", cite))
story.append(Spacer(1, 14))
# ===== SECTION 4: TPF vs PF =====
story.append(banner("4. TPF vs PF — Induction Head-to-Head (GORTEC / TAX 324)"))
story.append(Spacer(1, 6))
tpf_pf_rows = [
["Endpoint", "PF", "TPF"],
["Overall response rate", "59-61%", "80-83%"],
["Larynx preservation rate", "~57-60%", "~80%"],
["5-year overall survival", "42%", "52%"],
["Feeding tube / tracheostomy rate", "Baseline", "Not increased vs PF despite higher response"],
]
story.append(make_table(tpf_pf_rows, col_widths=[2.7*inch, 2.3*inch, 2.3*inch]))
story.append(Spacer(1, 16))
# ===== SECTION 5: KEY TAKEAWAYS =====
story.append(banner("5. Key Clinical Takeaways"))
story.append(Spacer(1, 8))
takeaways = [
"Concurrent cisplatin-chemoradiotherapy gives the highest larynx preservation (88%) and locoregional control "
"(78%) of any single strategy, but carries the highest toxicity burden (82% severe acute/late effects) — RTOG 91-11.",
"5-year overall survival is similar across RT alone, induction PF, and concurrent CRT (~54-56%). The real "
"differentiator between strategies is organ preservation and toxicity, not survival.",
"When induction chemotherapy is chosen, TPF is superior to PF: higher response rate (83% vs 61%), better "
"larynx preservation (~80% vs ~60%), and improved 5-year survival (52% vs 42%) — without increasing "
"feeding-tube or tracheostomy rates.",
"TPF carries greater hematologic toxicity (neutropenia, febrile neutropenia) and mucositis than PF — "
"patient selection (performance status, renal/hearing function, ability to tolerate triplet chemo) is critical.",
"Sequential therapy (induction TPF followed by concurrent chemoRT) has not shown a clear survival advantage "
"over concurrent chemoRT alone (PARADIGM trial) — added toxicity without proven benefit in unselected patients.",
"Carboplatin is substituted for cisplatin when renal impairment, hearing loss, or poor tolerance is a concern, "
"at the cost of somewhat reduced radiosensitizing potency.",
]
bullets = ListFlowable(
[ListItem(Paragraph(t, takeaway_body), spaceBefore=4) for t in takeaways],
bulletType='bullet', start='●', bulletColor=NAVY, bulletFontSize=8, leftIndent=16
)
story.append(bullets)
story.append(Spacer(1, 16))
# ===== SECTION 6: TOXICITY QUICK-CHECK =====
story.append(banner("6. Toxicity Quick-Check by Agent"))
story.append(Spacer(1, 6))
tox_rows = [
["Agent", "Watch For", "Monitoring"],
["Cisplatin", "Nephrotoxicity, ototoxicity, peripheral neuropathy, severe nausea",
"Creatinine/CrCl, audiology, Mg/electrolytes, aggressive hydration"],
["Carboplatin", "Myelosuppression (thrombocytopenia), hypersensitivity", "CBC, platelet count"],
["5-Fluorouracil", "Mucositis, diarrhea, myelosuppression, rare cardiotoxicity",
"Oral exam, CBC, DPD deficiency screen where available"],
["Methotrexate", "Myelosuppression, hepatotoxicity, mucositis, renal toxicity (high dose)",
"CBC, LFTs monthly, hydration/urine alkalinization for high-dose regimens"],
["Docetaxel / Paclitaxel", "Neutropenia, peripheral neuropathy, hypersensitivity, fluid retention (docetaxel)",
"CBC with differential, neuro exam, premedication for hypersensitivity"],
]
story.append(make_table(tox_rows, col_widths=[1.6*inch, 3.15*inch, 2.55*inch]))
story.append(Spacer(1, 12))
story.append(HRFlowable(width="100%", thickness=0.8, color=GRID_COLOR))
story.append(Spacer(1, 4))
story.append(Paragraph(
"This guide is a teaching summary compiled from Scott-Brown's Otorhinolaryngology Head & Neck Surgery, "
"Cummings Otolaryngology Head and Neck Surgery, K J Lee's Essential Otolaryngology, and Goodman & "
"Gilman's Pharmacological Basis of Therapeutics, supplemented by RTOG 91-11, GORTEC, and TAX 324 / EORTC "
"24971 trial data. Not a substitute for institutional protocols or current oncology guidelines — verify "
"dosing and eligibility against up-to-date clinical trial/NCCN sources before clinical use.", cite))
doc.build(story)
print("PDF created at", OUT)
~/hn-chemo-guide/build_guide.py
Successfully replaced 1 instance(s) of the string in '/tmp/workspace/hn-chemo-guide/build_guide.py'
rm -f /tmp/workspace/hn-chemo-guide/build_guide.py
~/hn-chemo-guide/build_guide.py
#!/usr/bin/env python3
"""
Quick Reference PDF Guide: Head & Neck Chemotherapy Regimens
Redesigned for clean, consistent, non-overflowing layout.
"""
from reportlab.lib.pagesizes import letter
from reportlab.lib import colors
from reportlab.lib.units import inch
from reportlab.platypus import (
SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
PageBreak, HRFlowable, ListFlowable, ListItem, Frame, BaseDocTemplate,
PageTemplate
)
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.enums import TA_CENTER, TA_LEFT
OUT = "/tmp/workspace/hn-chemo-guide/HN_Chemotherapy_Quick_Reference_Guide.pdf"
PAGE_W, PAGE_H = letter
MARGIN = 0.6 * inch
USABLE_W = PAGE_W - 2 * MARGIN # ~7.3in
NAVY = colors.HexColor('#0B3D66')
NAVY_LIGHT = colors.HexColor('#E7EEF5')
ACCENT = colors.HexColor('#C0392B')
GREY = colors.HexColor('#555555')
ROW_ALT = colors.HexColor('#F4F7FA')
GRID_COLOR = colors.HexColor('#C9D3DC')
styles = getSampleStyleSheet()
title_style = ParagraphStyle('TitleC', parent=styles['Title'], fontName='Helvetica-Bold',
fontSize=22, textColor=NAVY, alignment=TA_CENTER, spaceAfter=2)
subtitle_style = ParagraphStyle('Subtitle', parent=styles['Normal'], fontName='Helvetica',
fontSize=10.5, textColor=GREY, alignment=TA_CENTER, spaceAfter=2)
banner_text_style = ParagraphStyle('BannerText', parent=styles['Normal'], fontName='Helvetica-Bold',
fontSize=12.5, textColor=colors.white, leading=15)
body = ParagraphStyle('Body', parent=styles['Normal'], fontName='Helvetica', fontSize=8.6, leading=11.2)
body_bold = ParagraphStyle('BodyBold', parent=body, fontName='Helvetica-Bold', textColor=NAVY)
head_cell = ParagraphStyle('HeadCell', parent=styles['Normal'], fontName='Helvetica-Bold',
fontSize=8.6, leading=11, textColor=colors.white)
cite = ParagraphStyle('Cite', parent=styles['Normal'], fontName='Helvetica-Oblique', fontSize=7.6,
leading=10, textColor=GREY, spaceBefore=6)
takeaway_body = ParagraphStyle('TakeawayBody', parent=body, fontSize=9, leading=12.5)
footer_style = ParagraphStyle('Footer', parent=styles['Normal'], fontSize=7.5, textColor=GREY)
def banner(text):
"""Full-width colored section banner using a 1x1 table (reliable background rendering)."""
p = Paragraph(text, banner_text_style)
t = Table([[p]], colWidths=[USABLE_W])
t.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), 5),
('BOTTOMPADDING', (0, 0), (-1, -1), 5),
]))
return t
def make_table(rows, col_widths, header_style=head_cell, body_style=body, first_col_bold=True):
"""Build a table where every cell is a Paragraph (reliable wrapping, no overflow)."""
assert abs(sum(col_widths) - USABLE_W) < 0.05 * inch, \
f"Column widths sum {sum(col_widths)/inch:.2f}in != usable width {USABLE_W/inch:.2f}in"
data = []
header_row = [Paragraph(h, header_style) for h in rows[0]]
data.append(header_row)
for r in rows[1:]:
row_cells = []
for idx, val in enumerate(r):
if idx == 0 and first_col_bold:
row_cells.append(Paragraph(val, body_bold))
else:
row_cells.append(Paragraph(val, body_style))
data.append(row_cells)
t = Table(data, colWidths=col_widths, repeatRows=1)
style_cmds = [
('BACKGROUND', (0, 0), (-1, 0), NAVY),
('GRID', (0, 0), (-1, -1), 0.6, GRID_COLOR),
('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
('LEFTPADDING', (0, 0), (-1, -1), 5),
('RIGHTPADDING', (0, 0), (-1, -1), 5),
('TOPPADDING', (0, 0), (-1, -1), 4),
('BOTTOMPADDING', (0, 0), (-1, -1), 4),
]
for i in range(1, len(data)):
if i % 2 == 0:
style_cmds.append(('BACKGROUND', (0, i), (-1, i), ROW_ALT))
t.setStyle(TableStyle(style_cmds))
return t
def draw_page_decoration(canvas, doc_):
canvas.saveState()
canvas.setStrokeColor(GRID_COLOR)
canvas.setLineWidth(0.5)
canvas.line(MARGIN, 0.5 * inch, PAGE_W - MARGIN, 0.5 * inch)
canvas.setFont('Helvetica', 7.5)
canvas.setFillColor(GREY)
canvas.drawString(MARGIN, 0.34 * inch, "H&N Chemotherapy Quick Reference Guide")
canvas.drawRightString(PAGE_W - MARGIN, 0.34 * inch, f"Page {doc_.page}")
canvas.restoreState()
doc = BaseDocTemplate(
OUT, pagesize=letter,
topMargin=MARGIN, bottomMargin=0.7 * inch,
leftMargin=MARGIN, rightMargin=MARGIN,
title="H&N Chemotherapy Quick Reference Guide"
)
frame = Frame(MARGIN, 0.7 * inch, USABLE_W, PAGE_H - MARGIN - 0.7 * inch, id='normal')
doc.addPageTemplates([PageTemplate(id='main', frames=[frame], onPage=draw_page_decoration)])
story = []
# ===== TITLE BLOCK =====
story.append(Spacer(1, 4))
story.append(Paragraph("Head & Neck Chemotherapy", title_style))
story.append(Paragraph("Quick Reference Guide", title_style))
story.append(Spacer(1, 4))
story.append(Paragraph(
"Drug Mechanisms | Regimen Comparison | "
"Organ Preservation Outcomes | Toxicity", subtitle_style))
story.append(Spacer(1, 6))
story.append(HRFlowable(width="100%", thickness=1.4, color=NAVY))
story.append(Spacer(1, 12))
# ===== SECTION 1: DRUG CLASSES =====
story.append(banner("1. Core Chemotherapy Drug Classes & Mechanisms"))
story.append(Spacer(1, 6))
drug_rows = [
["Class", "Key Agents", "Mechanism of Action", "Dose-Limiting Toxicity"],
["Platinum agents", "Cisplatin, Carboplatin",
"DNA cross-linking at guanine N7 → blocks DNA/RNA/protein synthesis",
"Cisplatin: nephro-/ototoxicity. Carboplatin: myelosuppression"],
["Antimetabolites", "5-Fluorouracil, Methotrexate",
"5-FU inhibits thymidylate synthetase (S-phase). Methotrexate inhibits dihydrofolate reductase, blocking folate-dependent DNA/RNA synthesis",
"Mucositis, myelosuppression, hepatotoxicity (MTX)"],
["Taxanes", "Docetaxel, Paclitaxel",
"Stabilize microtubules against depolymerization → G2/M mitotic arrest",
"Neutropenia, peripheral neuropathy, hypersensitivity"],
["Vinca alkaloids", "Vincristine, Vinblastine",
"Inhibit tubulin polymerization → block microtubule assembly → M-phase arrest",
"Vincristine: neurotoxicity. Vinblastine: myelosuppression"],
["Cytotoxic antibiotics", "Bleomycin, Mitomycin C",
"Bleomycin causes DNA strand fragmentation; Mitomycin C cross-links DNA",
"Pulmonary fibrosis (bleomycin), myelosuppression"],
]
story.append(make_table(drug_rows, col_widths=[1.1*inch, 1.35*inch, 3.15*inch, 1.7*inch]))
story.append(Spacer(1, 14))
# ===== SECTION 2: REGIMEN COMPARISON =====
story.append(banner("2. Regimen Comparison: PF vs TPF vs Concurrent Chemoradiotherapy"))
story.append(Spacer(1, 6))
regimen_rows = [
["Feature", "PF (Induction)", "TPF (Induction)", "Concurrent CRT"],
["Drugs", "Cisplatin + 5-FU",
"Docetaxel + Cisplatin + 5-FU",
"Cisplatin (or carboplatin) given with radiotherapy"],
["Typical dosing", "Cisplatin 100 mg/m² D1; 5-FU CI ~1000 mg/m²/day D1-4/5; q3wk x2-3 cycles",
"Docetaxel 75 mg/m² D1 + Cisplatin 75-100 mg/m² D1 + 5-FU CI; q3wk x3 cycles",
"Cisplatin 100 mg/m² q21d x3 cycles, or weekly low-dose, concurrent with daily RT x6-7 wks"],
["Goal / setting", "Cytoreduction before RT/surgery; organ preservation",
"More potent cytoreduction than PF before definitive RT/surgery",
"Primary curative-intent or adjuvant treatment"],
["Response rate", "~61%", "~83% (superior to PF)", "N/A (not an induction endpoint)"],
["Organ preservation", "~75% (RTOG 91-11 induction arm)", "~80% (superior to PF)", "~88% (highest)"],
["Locoregional control", "~61%", "Improved vs PF", "~78% (highest)"],
["5-yr overall survival", "~42-54%", "~52%", "~54-56%"],
["Severe toxicity", "Moderate", "Higher than PF", "Highest (~82%)"],
]
story.append(make_table(regimen_rows, col_widths=[1.3*inch, 1.9*inch, 1.95*inch, 2.15*inch]))
story.append(Spacer(1, 6))
story.append(Paragraph(
"Sources: GORTEC (TPF vs PF), TAX 324 / EORTC 24971, RTOG 91-11; Cummings Otolaryngology Head and Neck "
"Surgery; Scott-Brown's Otorhinolaryngology Head & Neck Surgery.", cite))
story.append(PageBreak())
# ===== SECTION 3: RTOG 91-11 =====
story.append(banner("3. Organ Preservation Outcomes — RTOG 91-11 (3-Arm Laryngeal Trial)"))
story.append(Spacer(1, 6))
rtog_rows = [
["Arm", "Organ (Larynx) Preservation", "Locoregional Control", "5-yr Overall Survival", "Severe Toxicity"],
["RT alone", "70%", "56%", "54-56%", "Lowest of the three arms"],
["Induction PF → RT", "75%", "61%", "54-56%", "Moderate"],
["Concurrent Cisplatin + RT", "88% (highest)", "78% (highest)", "54-56% (similar across arms)", "82% (highest)"],
]
story.append(make_table(rtog_rows, col_widths=[1.55*inch, 1.55*inch, 1.4*inch, 1.5*inch, 1.3*inch]))
story.append(Spacer(1, 4))
story.append(Paragraph(
"Key point: overall survival is statistically similar across all three arms — concurrent chemoradiotherapy "
"wins on organ preservation and locoregional control, not on survival, and this benefit comes at the cost of "
"the highest toxicity.", cite))
story.append(Spacer(1, 14))
# ===== SECTION 4: TPF vs PF =====
story.append(banner("4. TPF vs PF — Induction Head-to-Head (GORTEC / TAX 324)"))
story.append(Spacer(1, 6))
tpf_pf_rows = [
["Endpoint", "PF", "TPF"],
["Overall response rate", "59-61%", "80-83%"],
["Larynx preservation rate", "~57-60%", "~80%"],
["5-year overall survival", "42%", "52%"],
["Feeding tube / tracheostomy rate", "Baseline", "Not increased vs PF despite higher response"],
]
story.append(make_table(tpf_pf_rows, col_widths=[2.7*inch, 2.3*inch, 2.3*inch]))
story.append(Spacer(1, 16))
# ===== SECTION 5: KEY TAKEAWAYS =====
story.append(banner("5. Key Clinical Takeaways"))
story.append(Spacer(1, 8))
takeaways = [
"Concurrent cisplatin-chemoradiotherapy gives the highest larynx preservation (88%) and locoregional control "
"(78%) of any single strategy, but carries the highest toxicity burden (82% severe acute/late effects) — RTOG 91-11.",
"5-year overall survival is similar across RT alone, induction PF, and concurrent CRT (~54-56%). The real "
"differentiator between strategies is organ preservation and toxicity, not survival.",
"When induction chemotherapy is chosen, TPF is superior to PF: higher response rate (83% vs 61%), better "
"larynx preservation (~80% vs ~60%), and improved 5-year survival (52% vs 42%) — without increasing "
"feeding-tube or tracheostomy rates.",
"TPF carries greater hematologic toxicity (neutropenia, febrile neutropenia) and mucositis than PF — "
"patient selection (performance status, renal/hearing function, ability to tolerate triplet chemo) is critical.",
"Sequential therapy (induction TPF followed by concurrent chemoRT) has not shown a clear survival advantage "
"over concurrent chemoRT alone (PARADIGM trial) — added toxicity without proven benefit in unselected patients.",
"Carboplatin is substituted for cisplatin when renal impairment, hearing loss, or poor tolerance is a concern, "
"at the cost of somewhat reduced radiosensitizing potency.",
]
bullets = ListFlowable(
[ListItem(Paragraph(t, takeaway_body), spaceBefore=4) for t in takeaways],
bulletType='bullet', start='circle', bulletColor=NAVY, bulletFontSize=6, leftIndent=16
)
story.append(bullets)
story.append(Spacer(1, 16))
# ===== SECTION 6: TOXICITY QUICK-CHECK =====
story.append(banner("6. Toxicity Quick-Check by Agent"))
story.append(Spacer(1, 6))
tox_rows = [
["Agent", "Watch For", "Monitoring"],
["Cisplatin", "Nephrotoxicity, ototoxicity, peripheral neuropathy, severe nausea",
"Creatinine/CrCl, audiology, Mg/electrolytes, aggressive hydration"],
["Carboplatin", "Myelosuppression (thrombocytopenia), hypersensitivity", "CBC, platelet count"],
["5-Fluorouracil", "Mucositis, diarrhea, myelosuppression, rare cardiotoxicity",
"Oral exam, CBC, DPD deficiency screen where available"],
["Methotrexate", "Myelosuppression, hepatotoxicity, mucositis, renal toxicity (high dose)",
"CBC, LFTs monthly, hydration/urine alkalinization for high-dose regimens"],
["Docetaxel / Paclitaxel", "Neutropenia, peripheral neuropathy, hypersensitivity, fluid retention (docetaxel)",
"CBC with differential, neuro exam, premedication for hypersensitivity"],
]
story.append(make_table(tox_rows, col_widths=[1.6*inch, 3.15*inch, 2.55*inch]))
story.append(Spacer(1, 12))
story.append(HRFlowable(width="100%", thickness=0.8, color=GRID_COLOR))
story.append(Spacer(1, 4))
story.append(Paragraph(
"This guide is a teaching summary compiled from Scott-Brown's Otorhinolaryngology Head & Neck Surgery, "
"Cummings Otolaryngology Head and Neck Surgery, K J Lee's Essential Otolaryngology, and Goodman & "
"Gilman's Pharmacological Basis of Therapeutics, supplemented by RTOG 91-11, GORTEC, and TAX 324 / EORTC "
"24971 trial data. Not a substitute for institutional protocols or current oncology guidelines — verify "
"dosing and eligibility against up-to-date clinical trial/NCCN sources before clinical use.", cite))
doc.build(story)
print("PDF created at", OUT)
cd /tmp/workspace/hn-chemo-guide && python3 build_guide.py
cd /tmp/workspace/hn-chemo-guide && python3 -c " import pdfplumber with pdfplumber.open('HN_Chemotherapy_Quick_Reference_Guide.pdf') as pdf: print('Pages:', len(pdf.pages)) for i, page in enumerate(pdf.pages): print(f'--- Page {i+1}: width={page.width}, height={page.height} ---') # check for any content exceeding page width for word in page.extract_words(): if word['x1'] > page.width + 1: print('OVERFLOW WORD:', word) tables = page.find_tables() for t in tables: x0,y0,x1,y1 = t.bbox status = 'OK' if x1 <= page.width+1 and x0>=-1 else 'OVERFLOW' print(f'table bbox x0={x0:.1f} x1={x1:.1f} width={x1-x0:.1f} status={status}') " ls -la /tmp/workspace/hn-chemo-guide/HN_Chemotherapy_Quick_Reference_Guide.pdf
HN Chemotherapy Quick Reference Guide
PDF Document · PDF
Paragraph instead of a raw string, so long mechanism/toxicity descriptions flow cleanly instead of getting cramped or cut off