make a pdf on cancer
mkdir -p /home/daytona/workspace/cancer-pdf && echo "dir ready"
~/cancer-pdf/build_cancer_pdf.py
from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.units import cm, mm
from reportlab.lib.enums import TA_LEFT, TA_CENTER, TA_JUSTIFY
from reportlab.platypus import (
SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
HRFlowable, PageBreak, KeepTogether
)
from reportlab.platypus.tableofcontents import TableOfContents
from reportlab.pdfgen import canvas
from reportlab.graphics.shapes import Drawing, Rect, String
from reportlab.graphics.charts.barcharts import VerticalBarChart
from reportlab.graphics import renderPDF
import os
OUTPUT = "/home/daytona/workspace/cancer-pdf/Cancer_Guide.pdf"
# ─── Colour palette ───────────────────────────────────────────────────────────
DARK_BLUE = colors.HexColor("#1A237E")
MID_BLUE = colors.HexColor("#1565C0")
LIGHT_BLUE = colors.HexColor("#E3F2FD")
ACCENT_RED = colors.HexColor("#B71C1C")
ACCENT_OR = colors.HexColor("#E65100")
TEAL = colors.HexColor("#00695C")
LIGHT_TEAL = colors.HexColor("#E0F2F1")
GREY_BG = colors.HexColor("#F5F5F5")
BORDER_GREY = colors.HexColor("#BDBDBD")
WHITE = colors.white
BLACK = colors.black
TEXT_GREY = colors.HexColor("#424242")
# ─── Page callbacks ───────────────────────────────────────────────────────────
def on_first_page(canvas, doc):
pass # Cover page has no header/footer
def on_later_pages(canvas, doc):
W, H = A4
canvas.saveState()
# Header bar
canvas.setFillColor(DARK_BLUE)
canvas.rect(0, H - 1.1*cm, W, 1.1*cm, fill=1, stroke=0)
canvas.setFillColor(WHITE)
canvas.setFont("Helvetica-Bold", 9)
canvas.drawString(1.5*cm, H - 0.75*cm, "Understanding Cancer")
canvas.setFont("Helvetica", 9)
canvas.drawRightString(W - 1.5*cm, H - 0.75*cm, "A Patient Guide")
# Footer
canvas.setFillColor(DARK_BLUE)
canvas.rect(0, 0, W, 0.8*cm, fill=1, stroke=0)
canvas.setFillColor(WHITE)
canvas.setFont("Helvetica", 8)
canvas.drawCentredString(W / 2, 0.25*cm, f"Page {doc.page}")
canvas.restoreState()
# ─── Styles ───────────────────────────────────────────────────────────────────
styles = getSampleStyleSheet()
def make_style(name, parent="Normal", **kw):
s = ParagraphStyle(name, parent=styles[parent], **kw)
return s
TITLE_STYLE = make_style("CoverTitle",
fontSize=38, textColor=WHITE, fontName="Helvetica-Bold",
leading=46, alignment=TA_CENTER, spaceAfter=6)
SUBTITLE_STYLE = make_style("CoverSub",
fontSize=16, textColor=colors.HexColor("#BBDEFB"),
fontName="Helvetica", leading=22, alignment=TA_CENTER)
H1 = make_style("H1",
fontSize=20, textColor=WHITE, fontName="Helvetica-Bold",
leading=26, spaceBefore=8, spaceAfter=4)
H2 = make_style("H2",
fontSize=14, textColor=DARK_BLUE, fontName="Helvetica-Bold",
leading=18, spaceBefore=14, spaceAfter=4, borderPadding=(0,0,2,0))
H3 = make_style("H3",
fontSize=11, textColor=MID_BLUE, fontName="Helvetica-Bold",
leading=15, spaceBefore=8, spaceAfter=2)
BODY = make_style("Body",
fontSize=10, textColor=TEXT_GREY, fontName="Helvetica",
leading=15, alignment=TA_JUSTIFY, spaceBefore=3, spaceAfter=3)
BULLET = make_style("Bullet",
fontSize=10, textColor=TEXT_GREY, fontName="Helvetica",
leading=14, leftIndent=18, bulletIndent=8, spaceBefore=2)
CALLOUT = make_style("Callout",
fontSize=10, textColor=DARK_BLUE, fontName="Helvetica-Oblique",
leading=14, alignment=TA_CENTER, spaceBefore=4, spaceAfter=4)
CAPTION = make_style("Caption",
fontSize=8, textColor=colors.HexColor("#757575"),
fontName="Helvetica-Oblique", leading=11, alignment=TA_CENTER)
HIGHLIGHT_BOX = make_style("HighlightBox",
fontSize=10, textColor=DARK_BLUE, fontName="Helvetica",
leading=14, leftIndent=10, rightIndent=10)
# ─── Helpers ─────────────────────────────────────────────────────────────────
def section_header(text, colour=DARK_BLUE):
"""Full-width coloured banner with white text."""
data = [[Paragraph(text, H1)]]
t = Table(data, colWidths=[17*cm])
t.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), colour),
("TOPPADDING", (0,0), (-1,-1), 8),
("BOTTOMPADDING", (0,0), (-1,-1), 8),
("LEFTPADDING", (0,0), (-1,-1), 14),
("RIGHTPADDING", (0,0), (-1,-1), 14),
("ROUNDEDCORNERS", [6]),
]))
return t
def info_box(text, bg=LIGHT_BLUE, border=MID_BLUE):
data = [[Paragraph(text, HIGHLIGHT_BOX)]]
t = Table(data, colWidths=[17*cm])
t.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), bg),
("BOX", (0,0), (-1,-1), 1.2, border),
("TOPPADDING", (0,0), (-1,-1), 8),
("BOTTOMPADDING", (0,0), (-1,-1), 8),
("LEFTPADDING", (0,0), (-1,-1), 12),
("RIGHTPADDING", (0,0), (-1,-1), 12),
]))
return t
def two_col(left_paras, right_paras):
col_w = 8.2*cm
left_content = "".join(f"<para>{p}</para>" for p in left_paras)
right_content = "".join(f"<para>{p}</para>" for p in right_paras)
left_cells = [[Paragraph(p, BODY)] for p in left_paras]
right_cells = [[Paragraph(p, BODY)] for p in right_paras]
# Pack into a 2-col table
rows = max(len(left_paras), len(right_paras))
data = []
for i in range(rows):
l = Paragraph(left_paras[i], BODY) if i < len(left_paras) else Paragraph("", BODY)
r = Paragraph(right_paras[i], BODY) if i < len(right_paras) else Paragraph("", BODY)
data.append([l, r])
t = Table(data, colWidths=[col_w, col_w])
t.setStyle(TableStyle([
("VALIGN", (0,0), (-1,-1), "TOP"),
("LEFTPADDING", (0,0), (0,-1), 0),
("RIGHTPADDING", (0,0), (0,-1), 10),
("LEFTPADDING", (1,0), (1,-1), 10),
("RIGHTPADDING", (1,0), (1,-1), 0),
]))
return t
def bullet_table(items, icon="•", col=MID_BLUE):
"""Styled bullet list rendered as a table."""
data = [[Paragraph(f'<font color="{col.hexval()}"><b>{icon}</b></font>', BODY),
Paragraph(item, BODY)] for item in items]
t = Table(data, colWidths=[0.6*cm, 16.4*cm])
t.setStyle(TableStyle([
("VALIGN", (0,0), (-1,-1), "TOP"),
("LEFTPADDING", (0,0), (-1,-1), 0),
("RIGHTPADDING", (0,0), (-1,-1), 0),
("TOPPADDING", (0,0), (-1,-1), 2),
("BOTTOMPADDING",(0,0), (-1,-1), 2),
]))
return t
def warning_box(text):
return info_box(f'<font color="#B71C1C"><b>⚠ Warning Signs:</b></font> {text}',
bg=colors.HexColor("#FFEBEE"), border=ACCENT_RED)
def tip_box(text):
return info_box(f'<font color="#1B5E20"><b>✔ Key Tip:</b></font> {text}',
bg=LIGHT_TEAL, border=TEAL)
# ─── Bar chart helper ─────────────────────────────────────────────────────────
def incidence_chart():
drawing = Drawing(400, 200)
bc = VerticalBarChart()
bc.x = 50; bc.y = 30
bc.height = 140; bc.width = 320
bc.data = [(22, 15, 14, 13, 8, 7, 7, 14)] # approximate % share
bc.strokeColor = colors.white
bc.fillColor = MID_BLUE
bc.bars[0].fillColor = MID_BLUE
bc.valueAxis.valueMin = 0
bc.valueAxis.valueMax = 25
bc.valueAxis.valueStep = 5
bc.valueAxis.labelTextFormat = '%d%%'
bc.categoryAxis.labels.boxAnchor = 'ne'
bc.categoryAxis.labels.dx = -2
bc.categoryAxis.labels.dy = -2
bc.categoryAxis.labels.angle = 30
bc.categoryAxis.labels.fontSize = 7
bc.categoryAxis.categoryNames = [
'Breast','Lung','Colorectal','Prostate',
'Stomach','Liver','Cervical','Other'
]
bc.barWidth = 14
drawing.add(bc)
title = String(200, 183, "Approximate Global Cancer Incidence by Type",
textAnchor="middle", fontSize=9,
fontName="Helvetica-Bold", fillColor=DARK_BLUE.hexval())
drawing.add(title)
return drawing
# ─── Build story ─────────────────────────────────────────────────────────────
story = []
# ══════════════════════════════════════════════════════════════════════════════
# COVER PAGE
# ══════════════════════════════════════════════════════════════════════════════
def cover_page(canvas, doc):
W, H = A4
canvas.saveState()
# Background gradient-like blocks
canvas.setFillColor(DARK_BLUE)
canvas.rect(0, 0, W, H, fill=1, stroke=0)
canvas.setFillColor(MID_BLUE)
canvas.rect(0, H*0.38, W, H*0.62, fill=1, stroke=0)
# Decorative ribbon
canvas.setFillColor(ACCENT_OR)
canvas.rect(0, H*0.375, W, 4, fill=1, stroke=0)
canvas.setFillColor(ACCENT_RED)
canvas.rect(0, H*0.375 + 4, W, 2, fill=1, stroke=0)
# Title
canvas.setFillColor(WHITE)
canvas.setFont("Helvetica-Bold", 46)
canvas.drawCentredString(W/2, H*0.6, "UNDERSTANDING")
canvas.setFont("Helvetica-Bold", 54)
canvas.drawCentredString(W/2, H*0.52, "CANCER")
# Subtitle
canvas.setFont("Helvetica", 16)
canvas.setFillColor(colors.HexColor("#BBDEFB"))
canvas.drawCentredString(W/2, H*0.46, "A Comprehensive Patient Guide")
# Divider line
canvas.setStrokeColor(colors.HexColor("#BBDEFB"))
canvas.setLineWidth(0.8)
canvas.line(2*cm, H*0.43, W-2*cm, H*0.43)
# Topics listed
canvas.setFont("Helvetica", 11)
canvas.setFillColor(colors.HexColor("#E3F2FD"))
topics = [
"What is Cancer? | Types of Cancer",
"Causes & Risk Factors | Warning Signs & Symptoms",
"Diagnosis | Treatment Options",
"Living With Cancer | Prevention & Screening"
]
y = H*0.40
for t in topics:
canvas.drawCentredString(W/2, y, t)
y -= 0.55*cm
# Bottom box
canvas.setFillColor(DARK_BLUE)
canvas.rect(0, 0, W, 3.2*cm, fill=1, stroke=0)
canvas.setFillColor(colors.HexColor("#BBDEFB"))
canvas.setFont("Helvetica-Oblique", 10)
canvas.drawCentredString(W/2, 2.2*cm, "For informational purposes only.")
canvas.drawCentredString(W/2, 1.5*cm, "Always consult a qualified healthcare professional for medical advice.")
canvas.setFillColor(WHITE)
canvas.setFont("Helvetica-Bold", 9)
canvas.drawCentredString(W/2, 0.7*cm, f"Published June 2026")
canvas.restoreState()
# ══════════════════════════════════════════════════════════════════════════════
# SECTION 1 — WHAT IS CANCER?
# ══════════════════════════════════════════════════════════════════════════════
story.append(section_header("1. What Is Cancer?"))
story.append(Spacer(1, 0.3*cm))
story.append(Paragraph("The Basics", H2))
story.append(Paragraph(
"Cancer is not a single disease but a broad term for more than 100 different conditions "
"that share one defining feature: uncontrolled cell growth. In a healthy body, cells "
"grow, divide, and die in an orderly way. Cancer begins when something goes wrong "
"in this process — a cell acquires mutations in its DNA that cause it to grow and divide "
"without stopping, eventually forming a lump called a <b>tumour</b>.",
BODY))
story.append(Spacer(1, 0.2*cm))
story.append(Paragraph(
"Not all tumours are dangerous. There are two broad categories:",
BODY))
story.append(Spacer(1, 0.1*cm))
benign_malignant = [
["Type", "Behaviour", "Spread?", "Life-threatening?"],
["Benign tumour", "Grows slowly; stays in one place", "No", "Rarely"],
["Malignant tumour","Invades nearby tissues", "Yes", "Often, if untreated"],
]
bm_table = Table(benign_malignant, colWidths=[4.5*cm, 6.5*cm, 3*cm, 3*cm])
bm_table.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,0), DARK_BLUE),
("TEXTCOLOR", (0,0), (-1,0), WHITE),
("FONTNAME", (0,0), (-1,0), "Helvetica-Bold"),
("FONTSIZE", (0,0), (-1,-1), 9),
("BACKGROUND", (0,1), (-1,1), LIGHT_BLUE),
("BACKGROUND", (0,2), (-1,2), WHITE),
("GRID", (0,0), (-1,-1), 0.5, BORDER_GREY),
("ALIGN", (0,0), (-1,-1), "CENTER"),
("VALIGN", (0,0), (-1,-1), "MIDDLE"),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING", (0,0), (-1,-1), 5),
]))
story.append(bm_table)
story.append(Spacer(1, 0.2*cm))
story.append(Paragraph("Metastasis", H2))
story.append(Paragraph(
"Malignant cells can break away from the original (primary) tumour, travel through the "
"bloodstream or lymphatic system, and form new tumours — called <b>metastases</b> — in "
"distant parts of the body such as the liver, lungs, bones, or brain. This process is "
"called <b>metastasis</b> and is what makes cancer particularly dangerous. Treatment is "
"generally harder once metastasis has occurred, which is why early detection matters so much.",
BODY))
# ══════════════════════════════════════════════════════════════════════════════
# SECTION 2 — TYPES OF CANCER
# ══════════════════════════════════════════════════════════════════════════════
story.append(PageBreak())
story.append(section_header("2. Types of Cancer", colour=MID_BLUE))
story.append(Spacer(1, 0.3*cm))
story.append(Paragraph(
"Cancers are grouped by the type of cell or tissue they originate from. "
"The five main categories are:", BODY))
story.append(Spacer(1, 0.15*cm))
types_data = [
["Category", "Origin", "Examples"],
["Carcinoma", "Epithelial cells (skin, organs)", "Breast, lung, colon, prostate, cervical"],
["Sarcoma", "Connective tissue (bone, muscle, fat)", "Osteosarcoma, rhabdomyosarcoma, liposarcoma"],
["Leukaemia", "Blood-forming cells in bone marrow", "ALL, AML, CLL, CML"],
["Lymphoma", "Lymphatic system cells", "Hodgkin lymphoma, Non-Hodgkin lymphoma"],
["CNS tumours", "Brain and spinal cord cells", "Glioblastoma, meningioma, medulloblastoma"],
]
t2 = Table(types_data, colWidths=[3.5*cm, 6.5*cm, 7*cm])
t2.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,0), DARK_BLUE),
("TEXTCOLOR", (0,0), (-1,0), WHITE),
("FONTNAME", (0,0), (-1,0), "Helvetica-Bold"),
("FONTSIZE", (0,0), (-1,-1), 9),
("ROWBACKGROUNDS", (0,1), (-1,-1), [GREY_BG, WHITE]),
("GRID", (0,0), (-1,-1), 0.5, BORDER_GREY),
("ALIGN", (0,0), (-1,-1), "LEFT"),
("VALIGN", (0,0), (-1,-1), "MIDDLE"),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING", (0,0), (-1,-1), 5),
("LEFTPADDING", (0,0), (-1,-1), 6),
]))
story.append(t2)
story.append(Spacer(1, 0.3*cm))
story.append(Paragraph("Global Incidence", H2))
story.append(Paragraph(
"Worldwide, the most commonly diagnosed cancers are breast, lung, colorectal, and prostate. "
"The chart below shows their approximate share of new cancer cases globally.",
BODY))
story.append(Spacer(1, 0.15*cm))
chart = incidence_chart()
story.append(chart)
story.append(Paragraph("Source: estimated from WHO/GLOBOCAN global cancer statistics.", CAPTION))
# ══════════════════════════════════════════════════════════════════════════════
# SECTION 3 — CAUSES & RISK FACTORS
# ══════════════════════════════════════════════════════════════════════════════
story.append(PageBreak())
story.append(section_header("3. Causes & Risk Factors", colour=ACCENT_OR))
story.append(Spacer(1, 0.3*cm))
story.append(Paragraph(
"Cancer is caused by changes (mutations) in the DNA of cells. These mutations can be "
"inherited or acquired during a lifetime. Many factors increase the chance of mutations occurring:",
BODY))
story.append(Spacer(1, 0.2*cm))
story.append(Paragraph("Lifestyle Factors", H2))
story.append(bullet_table([
"<b>Tobacco use</b> — the single largest preventable cause of cancer; responsible for ~22% of cancer deaths globally. Smoking causes cancers of the lung, throat, bladder, kidney, pancreas, and more.",
"<b>Alcohol</b> — increases risk of cancers of the mouth, throat, oesophagus, liver, breast, and colon.",
"<b>Diet</b> — a diet high in processed and red meat increases colorectal cancer risk; low intake of fruits and vegetables is also linked to several cancers.",
"<b>Obesity</b> — excess body fat raises risk for at least 13 types of cancer, including breast (post-menopausal), uterine, and kidney.",
"<b>Physical inactivity</b> — regular exercise reduces the risk of colon, breast, and endometrial cancers.",
]))
story.append(Spacer(1, 0.2*cm))
story.append(Paragraph("Environmental & Occupational Factors", H2))
story.append(bullet_table([
"<b>UV radiation</b> — excessive sun exposure and sunbed use are the primary cause of skin cancers (basal cell carcinoma, squamous cell carcinoma, melanoma).",
"<b>Ionising radiation</b> — X-rays, gamma rays, and radon gas can damage DNA. Medical imaging uses the lowest possible dose.",
"<b>Air pollution</b> — classified as a Group 1 carcinogen by the IARC; linked to lung cancer.",
"<b>Carcinogens at work</b> — asbestos (mesothelioma, lung cancer), benzene (leukaemia), formaldehyde, and certain pesticides.",
]))
story.append(Spacer(1, 0.2*cm))
story.append(Paragraph("Biological & Genetic Factors", H2))
story.append(bullet_table([
"<b>Age</b> — the risk of cancer rises significantly after age 50 because DNA damage accumulates over a lifetime.",
"<b>Family history & inherited mutations</b> — BRCA1/BRCA2 gene mutations greatly increase breast and ovarian cancer risk. Lynch syndrome raises colorectal cancer risk.",
"<b>Infectious agents</b> — Human Papillomavirus (HPV) causes nearly all cervical cancers; Hepatitis B and C viruses cause liver cancer; H. pylori bacteria cause stomach cancer; HIV weakens immunity and raises lymphoma risk.",
"<b>Hormones</b> — prolonged exposure to oestrogen (e.g., hormone replacement therapy, early menstruation) is a risk factor for breast and uterine cancers.",
]))
story.append(Spacer(1, 0.2*cm))
story.append(tip_box(
"About 30-50% of all cancer cases are preventable by avoiding known risk factors and "
"implementing evidence-based prevention strategies. — World Health Organization"))
# ══════════════════════════════════════════════════════════════════════════════
# SECTION 4 — WARNING SIGNS & SYMPTOMS
# ══════════════════════════════════════════════════════════════════════════════
story.append(PageBreak())
story.append(section_header("4. Warning Signs & Symptoms", colour=ACCENT_RED))
story.append(Spacer(1, 0.3*cm))
story.append(Paragraph(
"Cancer can cause a wide variety of symptoms depending on where it is located and how "
"advanced it is. Many early cancers cause no symptoms at all — which is why screening is "
"important. However, the following warning signs should prompt a visit to a doctor:",
BODY))
story.append(Spacer(1, 0.2*cm))
story.append(warning_box(
"Any of the symptoms below that are new, persistent, or unexplained — lasting more than "
"2-3 weeks — deserve medical attention. Early evaluation does not mean you have cancer, "
"but it does mean you can find out."))
story.append(Spacer(1, 0.2*cm))
story.append(Paragraph("General Symptoms", H2))
story.append(bullet_table([
"Unexplained weight loss (more than 5 kg without trying)",
"Persistent fatigue not relieved by rest",
"Fever that keeps returning without explanation",
"Night sweats",
], icon="!"))
story.append(Paragraph("Local / Organ-Specific Symptoms", H2))
local_symp = [
["Area", "Possible Symptoms"],
["Skin", "New mole or change in an existing mole; sore that won't heal; unusual lump under skin"],
["Breast", "New lump; change in breast shape or size; nipple discharge or inversion; skin dimpling"],
["Bowel", "Change in bowel habits lasting >4 weeks; blood in stool; unexplained anaemia"],
["Bladder", "Blood in urine (painless haematuria); frequent or urgent urination"],
["Lungs", "Persistent cough lasting >3 weeks; coughing up blood; unexplained breathlessness"],
["Throat/Mouth","Hoarse voice >3 weeks; difficulty swallowing; persistent mouth sore or white/red patch"],
["Prostate", "Difficulty urinating; weak urine flow; blood in urine or semen"],
["Uterus/Ovary","Post-menopausal bleeding; persistent bloating; pelvic pain"],
]
t4 = Table(local_symp, colWidths=[3.5*cm, 13.5*cm])
t4.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,0), ACCENT_RED),
("TEXTCOLOR", (0,0), (-1,0), WHITE),
("FONTNAME", (0,0), (-1,0), "Helvetica-Bold"),
("FONTSIZE", (0,0), (-1,-1), 9),
("ROWBACKGROUNDS", (0,1), (-1,-1), [colors.HexColor("#FFEBEE"), WHITE]),
("GRID", (0,0), (-1,-1), 0.5, BORDER_GREY),
("VALIGN", (0,0), (-1,-1), "MIDDLE"),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING", (0,0), (-1,-1), 5),
("LEFTPADDING", (0,0), (-1,-1), 6),
]))
story.append(t4)
# ══════════════════════════════════════════════════════════════════════════════
# SECTION 5 — DIAGNOSIS
# ══════════════════════════════════════════════════════════════════════════════
story.append(PageBreak())
story.append(section_header("5. Diagnosis", colour=TEAL))
story.append(Spacer(1, 0.3*cm))
story.append(Paragraph(
"Diagnosing cancer requires a combination of clinical assessment and tests. The diagnostic "
"process typically involves the following steps:",
BODY))
story.append(Spacer(1, 0.2*cm))
story.append(Paragraph("Step 1 — Initial Assessment", H2))
story.append(Paragraph(
"Your doctor takes a detailed history of your symptoms, family history, lifestyle, and "
"performs a physical examination. This helps decide which investigations are needed.", BODY))
story.append(Paragraph("Step 2 — Imaging Tests", H2))
story.append(bullet_table([
"<b>X-ray</b> — useful for bone and chest abnormalities.",
"<b>Ultrasound</b> — uses sound waves to examine soft tissue organs (thyroid, breast, abdomen).",
"<b>CT scan (Computed Tomography)</b> — produces detailed cross-sectional images of the body; useful for detecting tumours and staging.",
"<b>MRI (Magnetic Resonance Imaging)</b> — excellent detail for soft tissues such as brain, spine, and pelvic organs.",
"<b>PET scan (Positron Emission Tomography)</b> — detects metabolically active (rapidly dividing) cells; used to check if cancer has spread.",
"<b>Mammography</b> — specialised X-ray of breast tissue; the primary screening tool for breast cancer.",
]))
story.append(Paragraph("Step 3 — Blood & Lab Tests", H2))
story.append(bullet_table([
"<b>Full blood count (FBC)</b> — checks for anaemia, abnormal white cells (leukaemia).",
"<b>Tumour markers</b> — proteins elevated in certain cancers, e.g. PSA (prostate), CA-125 (ovarian), CEA (colorectal). Markers alone cannot diagnose cancer but help monitor treatment.",
"<b>Liver function tests, kidney function, LDH</b> — assess organ health and potential spread.",
]))
story.append(Paragraph("Step 4 — Biopsy (Most Important Step)", H2))
story.append(info_box(
"A <b>biopsy</b> is the removal of a small sample of tissue for examination under a "
"microscope by a pathologist. It is the definitive way to diagnose cancer. Types include "
"needle biopsy, surgical biopsy, endoscopic biopsy, and liquid biopsy (blood test for "
"tumour DNA).",
bg=LIGHT_TEAL, border=TEAL))
story.append(Spacer(1, 0.2*cm))
story.append(Paragraph("Cancer Staging", H2))
story.append(Paragraph(
"Once cancer is confirmed, it is staged to determine how far it has spread. "
"The TNM system is the most widely used:", BODY))
story.append(Spacer(1, 0.1*cm))
staging_data = [
["Stage", "Description"],
["Stage 0", "Cancer cells present but have not invaded nearby tissue (carcinoma in situ)"],
["Stage I", "Small tumour confined to the organ of origin; no lymph node involvement"],
["Stage II", "Larger tumour and/or limited spread to nearby lymph nodes"],
["Stage III", "Extensive local spread; significant lymph node involvement"],
["Stage IV", "Cancer has metastasised to distant organs (e.g. liver, lungs, bones, brain)"],
]
ts = Table(staging_data, colWidths=[2.5*cm, 14.5*cm])
ts.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,0), TEAL),
("TEXTCOLOR", (0,0), (-1,0), WHITE),
("FONTNAME", (0,0), (-1,0), "Helvetica-Bold"),
("FONTSIZE", (0,0), (-1,-1), 9),
("ROWBACKGROUNDS", (0,1), (-1,-1), [LIGHT_TEAL, WHITE]),
("GRID", (0,0), (-1,-1), 0.5, BORDER_GREY),
("VALIGN", (0,0), (-1,-1), "MIDDLE"),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING", (0,0), (-1,-1), 5),
("LEFTPADDING", (0,0), (-1,-1), 6),
]))
story.append(ts)
# ══════════════════════════════════════════════════════════════════════════════
# SECTION 6 — TREATMENT
# ══════════════════════════════════════════════════════════════════════════════
story.append(PageBreak())
story.append(section_header("6. Treatment Options", colour=DARK_BLUE))
story.append(Spacer(1, 0.3*cm))
story.append(Paragraph(
"Treatment depends on the type of cancer, stage, location, your overall health, and your "
"personal preferences. Most people receive a combination of treatments, overseen by a "
"multidisciplinary team (MDT) of specialists.",
BODY))
story.append(Spacer(1, 0.2*cm))
tx_options = [
("Surgery", MID_BLUE, [
"Removes the tumour and some surrounding healthy tissue.",
"May be curative for early-stage, localised cancers.",
"Also used for debulking (reducing tumour size) or palliative relief.",
"Minimally invasive techniques (laparoscopy, robotic surgery) reduce recovery time.",
]),
("Radiotherapy", ACCENT_RED, [
"Uses high-energy X-rays or particles to destroy cancer cells.",
"Can be delivered externally (external beam) or internally (brachytherapy).",
"Often used alongside surgery or chemotherapy.",
"Common side effects: fatigue, skin changes in the treated area.",
]),
("Chemotherapy", TEAL, [
"Uses drugs to kill rapidly dividing cells throughout the body.",
"Effective against cancers that have spread or for systemic disease like leukaemia.",
"Given in cycles to allow the body to recover between treatments.",
"Side effects can include nausea, hair loss, fatigue, and increased infection risk.",
]),
("Targeted Therapy", ACCENT_OR, [
"Drugs that specifically target molecular changes in cancer cells (e.g. HER2 in breast cancer, EGFR in lung cancer).",
"Generally have fewer side effects than conventional chemotherapy.",
"Requires prior testing to confirm that the target molecule is present.",
]),
("Immunotherapy", colors.HexColor("#4A148C"), [
"Helps the immune system recognise and destroy cancer cells.",
"Checkpoint inhibitors (e.g. pembrolizumab, nivolumab) block proteins that cancer uses to hide from immune cells.",
"CAR-T cell therapy re-engineers a patient's own T-cells to attack cancer.",
"Particularly effective in melanoma, lung cancer, and some leukaemias.",
]),
("Hormone Therapy", colors.HexColor("#1B5E20"), [
"Reduces or blocks hormones that fuel certain cancers (breast, prostate).",
"Examples: tamoxifen, aromatase inhibitors (breast); LHRH agonists, enzalutamide (prostate).",
]),
("Bone Marrow / Stem Cell Transplant", colors.HexColor("#006064"), [
"Replaces diseased bone marrow with healthy stem cells.",
"Used for leukaemia, lymphoma, and myeloma after high-dose chemotherapy.",
]),
]
for name, col, pts in tx_options:
story.append(KeepTogether([
Paragraph(name, ParagraphStyle("TxH", parent=H2, textColor=col)),
bullet_table(pts),
Spacer(1, 0.15*cm),
]))
story.append(tip_box(
"Palliative care focuses on relieving symptoms and improving quality of life at any stage "
"of cancer. It can be given alongside curative treatment, not only at end of life."))
# ══════════════════════════════════════════════════════════════════════════════
# SECTION 7 — LIVING WITH CANCER
# ══════════════════════════════════════════════════════════════════════════════
story.append(PageBreak())
story.append(section_header("7. Living With Cancer", colour=colors.HexColor("#4A148C")))
story.append(Spacer(1, 0.3*cm))
story.append(Paragraph(
"A cancer diagnosis affects every aspect of life — physical, emotional, social, and financial. "
"The following guidance can help patients and their families cope.",
BODY))
story.append(Spacer(1, 0.2*cm))
story.append(Paragraph("Physical Wellbeing", H2))
story.append(bullet_table([
"<b>Nutrition:</b> Aim for a well-balanced diet rich in fruits, vegetables, whole grains, and lean protein. A dietitian can advise on managing treatment-related nausea, weight changes, or mouth sores.",
"<b>Exercise:</b> Gentle to moderate activity (e.g. walking, yoga) reduces fatigue, improves mood, and can enhance treatment outcomes. Always check with your team first.",
"<b>Rest and sleep:</b> Cancer-related fatigue is very common. Rest when needed, but short daily activity helps maintain energy levels.",
"<b>Managing side effects:</b> Anti-nausea drugs, growth factors, and supportive medications are available — do not suffer in silence. Tell your team about all symptoms.",
]))
story.append(Paragraph("Emotional & Mental Health", H2))
story.append(bullet_table([
"It is normal to experience anxiety, depression, anger, or grief. These are understandable responses.",
"Seek support from a psychologist, counsellor, or social worker with experience in oncology.",
"Cancer support groups (in-person or online) provide connection with people who understand your experience.",
"Mindfulness, meditation, and relaxation techniques can reduce anxiety and improve sleep.",
]))
story.append(Paragraph("Practical & Social Support", H2))
story.append(bullet_table([
"<b>Financial support:</b> Ask the social work team about disability benefits, sick pay, and charitable grants.",
"<b>Work and legal rights:</b> In many countries, cancer patients have legal protection against discrimination at work.",
"<b>Communication:</b> Being open with family and close friends about your needs can strengthen support networks.",
"<b>Fertility:</b> Some treatments affect fertility. If this is a concern, discuss fertility preservation options before starting treatment.",
]))
story.append(Paragraph("After Treatment — Survivorship", H2))
story.append(Paragraph(
"After completing treatment, regular follow-up appointments monitor for recurrence and "
"manage late effects of treatment. Survivorship care plans outline what tests are needed "
"and how often. Many people go on to live full, productive lives after cancer.",
BODY))
# ══════════════════════════════════════════════════════════════════════════════
# SECTION 8 — PREVENTION & SCREENING
# ══════════════════════════════════════════════════════════════════════════════
story.append(PageBreak())
story.append(section_header("8. Prevention & Screening", colour=TEAL))
story.append(Spacer(1, 0.3*cm))
story.append(Paragraph("Prevention", H2))
story.append(Paragraph(
"The WHO estimates that 30-50% of cancers can be prevented. Evidence-based measures include:",
BODY))
story.append(Spacer(1, 0.1*cm))
story.append(bullet_table([
"<b>Do not smoke</b> — if you smoke, quitting at any age reduces cancer risk.",
"<b>Limit alcohol</b> — no amount is completely safe, but staying within low-risk guidelines substantially reduces risk.",
"<b>Maintain a healthy weight</b> — through a balanced diet and regular physical activity.",
"<b>Protect your skin</b> — use sunscreen (SPF 30+), seek shade, avoid sunbeds.",
"<b>Eat well</b> — a diet high in fibre, fruits, and vegetables and low in processed meat lowers cancer risk.",
"<b>Exercise regularly</b> — at least 150 minutes of moderate activity per week.",
"<b>Vaccination</b> — HPV vaccine prevents cervical (and other) cancers; Hepatitis B vaccine prevents liver cancer.",
"<b>Reduce environmental exposures</b> — test your home for radon gas; limit exposure to known carcinogens at work.",
]))
story.append(Spacer(1, 0.2*cm))
story.append(Paragraph("Screening Programmes", H2))
story.append(Paragraph(
"Screening looks for cancer before symptoms appear, when treatment is most effective. "
"The following are widely recommended:", BODY))
story.append(Spacer(1, 0.1*cm))
screen_data = [
["Cancer Type", "Screening Test", "Who / When"],
["Breast", "Mammography", "Women 50-74 yrs, every 2 years (varies by country)"],
["Cervical", "Pap smear + HPV test", "Women 25-65 yrs, every 3-5 years"],
["Colorectal", "Faecal immunochemical test (FIT)","Adults 50-74 yrs, every 1-2 years; colonoscopy if positive"],
["Lung", "Low-dose CT scan", "Heavy smokers 55-80 yrs (eligibility criteria vary)"],
["Prostate", "PSA blood test", "Discuss with GP; no universal recommendation; age 50+"],
["Skin", "Skin self-exam + dermatologist", "People with high UV exposure, family history of melanoma"],
]
tsc = Table(screen_data, colWidths=[3.2*cm, 5.5*cm, 8.3*cm])
tsc.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,0), TEAL),
("TEXTCOLOR", (0,0), (-1,0), WHITE),
("FONTNAME", (0,0), (-1,0), "Helvetica-Bold"),
("FONTSIZE", (0,0), (-1,-1), 9),
("ROWBACKGROUNDS", (0,1), (-1,-1), [LIGHT_TEAL, WHITE]),
("GRID", (0,0), (-1,-1), 0.5, BORDER_GREY),
("VALIGN", (0,0), (-1,-1), "MIDDLE"),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING", (0,0), (-1,-1), 5),
("LEFTPADDING", (0,0), (-1,-1), 6),
]))
story.append(tsc)
story.append(Spacer(1, 0.2*cm))
story.append(tip_box(
"Screening saves lives. If you are in the recommended age range, take part in "
"your national screening programme. Speak to your GP if you have specific concerns."))
# ══════════════════════════════════════════════════════════════════════════════
# SECTION 9 — QUICK REFERENCE
# ══════════════════════════════════════════════════════════════════════════════
story.append(PageBreak())
story.append(section_header("9. Quick Reference & Key Facts"))
story.append(Spacer(1, 0.3*cm))
facts = [
"Cancer is the second leading cause of death globally, responsible for ~10 million deaths per year.",
"The most common cancers worldwide are breast, lung, colorectal, and prostate.",
"Tobacco use accounts for approximately 22% of all cancer deaths.",
"At least 1 in 3 cancers can be prevented through lifestyle changes.",
"HPV vaccination can prevent up to 90% of cervical cancers.",
"5-year survival rates for many common cancers have doubled over the past 40 years thanks to improved screening and treatment.",
"Leukaemia survival rates in children have risen from 10% in the 1960s to over 90% today for ALL.",
"Early detection is the single most powerful tool available to improve cancer outcomes.",
]
story.append(bullet_table(facts, icon="★", col=ACCENT_OR))
story.append(Spacer(1, 0.3*cm))
story.append(Paragraph("Useful Organisations & Resources", H2))
orgs = [
["Organisation", "Website", "Focus"],
["World Health Organization (WHO)", "who.int/cancer", "Global cancer data and policy"],
["American Cancer Society", "cancer.org", "Patient information and support (USA)"],
["Cancer Research UK", "cancerresearchuk.org", "Research, prevention, patient support (UK)"],
["International Agency for Research\non Cancer (IARC)", "iarc.fr", "Carcinogen classification and epidemiology"],
["National Cancer Institute (USA)", "cancer.gov", "Clinical trials, treatment info"],
["Macmillan Cancer Support", "macmillan.org.uk", "Practical and emotional support (UK)"],
]
to = Table(orgs, colWidths=[5.5*cm, 5*cm, 6.5*cm])
to.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,0), DARK_BLUE),
("TEXTCOLOR", (0,0), (-1,0), WHITE),
("FONTNAME", (0,0), (-1,0), "Helvetica-Bold"),
("FONTSIZE", (0,0), (-1,-1), 8),
("ROWBACKGROUNDS", (0,1), (-1,-1), [LIGHT_BLUE, WHITE]),
("GRID", (0,0), (-1,-1), 0.5, BORDER_GREY),
("VALIGN", (0,0), (-1,-1), "MIDDLE"),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING", (0,0), (-1,-1), 5),
("LEFTPADDING", (0,0), (-1,-1), 6),
]))
story.append(to)
story.append(Spacer(1, 0.4*cm))
story.append(info_box(
"<b>Disclaimer:</b> This document is intended for general educational purposes only. "
"It does not constitute medical advice, diagnosis, or treatment recommendations. "
"Always consult a qualified healthcare professional with any questions regarding a "
"medical condition or treatment.",
bg=colors.HexColor("#FFF8E1"), border=ACCENT_OR))
# ─── Build PDF ────────────────────────────────────────────────────────────────
doc = SimpleDocTemplate(
OUTPUT,
pagesize=A4,
leftMargin=2*cm, rightMargin=2*cm,
topMargin=1.8*cm, bottomMargin=1.6*cm,
title="Understanding Cancer: A Patient Guide",
author="Orris Medical AI",
subject="Cancer Education",
)
doc.build(
story,
onFirstPage=cover_page,
onLaterPages=on_later_pages,
)
print(f"PDF created: {OUTPUT}")
cd /home/daytona/workspace/cancer-pdf && python build_cancer_pdf.py 2>&1
ls -lh /home/daytona/workspace/cancer-pdf/Cancer_Guide.pdf
Cancer Guide
PDF Document · PDF
| Section | Content |
|---|---|
| 1. What Is Cancer? | Definition, benign vs. malignant, metastasis |
| 2. Types of Cancer | Carcinoma, sarcoma, leukaemia, lymphoma, CNS tumours + incidence bar chart |
| 3. Causes & Risk Factors | Lifestyle, environmental, biological/genetic |
| 4. Warning Signs & Symptoms | General + organ-specific symptom table |
| 5. Diagnosis | Imaging, blood tests, biopsy, staging table (Stage 0-IV) |
| 6. Treatment Options | Surgery, radiotherapy, chemo, targeted therapy, immunotherapy, hormone therapy |
| 7. Living With Cancer | Physical wellbeing, mental health, survivorship |
| 8. Prevention & Screening | Evidence-based prevention steps + screening table by cancer type |
| 9. Quick Reference | Key facts, useful organisations & resources |