Make pdf on liver abscess in 5yr old male

Reading File
Finding Sources
Finding Sources
Searching PubMed

"liver abscess" AND child

Now let me read the main content from the best sources in parallel:
Reading File
Reading File
Reading File
Reading Article

PMID: 34794678

Now let me get more content from Current Surgical Therapy (pediatric details) and Sleisenger, and also search for images:
Reading File
Reading File
Searching Images

liver abscess ultrasound CT scan child pediatric

Excellent content gathered. Now let me read the PDF skill and get more content on amebic liver abscess and pediatric aspects:
Reading File
Reading File
Finding Sources
Reading File
Reading File
I now have comprehensive content. Let me update todos and generate the PDF:
Running Command

mkdir -p /home/daytona/workspace/liver-abscess-pdf

Writing File

~/liver-abscess-pdf/generate_pdf.py

#!/usr/bin/env python3
"""Generate a comprehensive PDF on Liver Abscess in a 5-year-old male."""

import requests
import io
from reportlab.lib.pagesizes import A4
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.colors import HexColor, white, black
from reportlab.lib.units import cm, mm
from reportlab.platypus import (
    SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
    HRFlowable, PageBreak, Image, KeepTogether
)
from reportlab.lib.enums import TA_LEFT, TA_CENTER, TA_JUSTIFY
from reportlab.platypus.flowables import BalancedColumns
from PIL import Image as PILImage
import os

OUTPUT_PATH = "/home/daytona/workspace/liver-abscess-pdf/Liver_Abscess_5yr_Male.pdf"

# ── Colors ──────────────────────────────────────────────────────────────────
DARK_BLUE   = HexColor("#1a3a5c")
MID_BLUE    = HexColor("#2e6da4")
LIGHT_BLUE  = HexColor("#d6e8f7")
ACCENT_RED  = HexColor("#c0392b")
ACCENT_ORANGE = HexColor("#e67e22")
LIGHT_GRAY  = HexColor("#f5f5f5")
MED_GRAY    = HexColor("#cccccc")
DARK_GRAY   = HexColor("#444444")
GREEN       = HexColor("#27ae60")
WARN_BG     = HexColor("#fff3e0")
WARN_BORDER = HexColor("#ff9800")

# ── Styles ───────────────────────────────────────────────────────────────────
def build_styles():
    styles = getSampleStyleSheet()

    styles.add(ParagraphStyle(
        'CoverTitle', fontName='Helvetica-Bold', fontSize=26,
        textColor=white, alignment=TA_CENTER, spaceAfter=6, leading=32
    ))
    styles.add(ParagraphStyle(
        'CoverSub', fontName='Helvetica', fontSize=14,
        textColor=HexColor("#cce0f5"), alignment=TA_CENTER, spaceAfter=4, leading=18
    ))
    styles.add(ParagraphStyle(
        'CoverPatient', fontName='Helvetica-Bold', fontSize=13,
        textColor=HexColor("#ffe082"), alignment=TA_CENTER, spaceAfter=4, leading=16
    ))
    styles.add(ParagraphStyle(
        'SectionHeader', fontName='Helvetica-Bold', fontSize=14,
        textColor=white, spaceBefore=10, spaceAfter=4, leading=18,
        backColor=DARK_BLUE, leftIndent=-8, rightIndent=-8, borderPadding=(4,8,4,8)
    ))
    styles.add(ParagraphStyle(
        'SubHeader', fontName='Helvetica-Bold', fontSize=11,
        textColor=DARK_BLUE, spaceBefore=8, spaceAfter=3, leading=14
    ))
    styles.add(ParagraphStyle(
        'Body', fontName='Helvetica', fontSize=10,
        textColor=DARK_GRAY, spaceAfter=5, leading=14, alignment=TA_JUSTIFY
    ))
    styles.add(ParagraphStyle(
        'BulletBody', fontName='Helvetica', fontSize=10,
        textColor=DARK_GRAY, spaceAfter=3, leading=13,
        leftIndent=14, bulletIndent=0
    ))
    styles.add(ParagraphStyle(
        'TableHeader', fontName='Helvetica-Bold', fontSize=9,
        textColor=white, alignment=TA_CENTER
    ))
    styles.add(ParagraphStyle(
        'TableCell', fontName='Helvetica', fontSize=9,
        textColor=DARK_GRAY, alignment=TA_LEFT, leading=12
    ))
    styles.add(ParagraphStyle(
        'Caption', fontName='Helvetica-Oblique', fontSize=8,
        textColor=DARK_GRAY, alignment=TA_CENTER, spaceAfter=6
    ))
    styles.add(ParagraphStyle(
        'Warning', fontName='Helvetica-Bold', fontSize=10,
        textColor=ACCENT_RED, spaceAfter=3, leading=13
    ))
    styles.add(ParagraphStyle(
        'SmallBody', fontName='Helvetica', fontSize=8.5,
        textColor=DARK_GRAY, spaceAfter=3, leading=12
    ))
    styles.add(ParagraphStyle(
        'FooterStyle', fontName='Helvetica', fontSize=7.5,
        textColor=HexColor("#888888"), alignment=TA_CENTER
    ))
    styles.add(ParagraphStyle(
        'HighlightBox', fontName='Helvetica', fontSize=10,
        textColor=DARK_GRAY, spaceAfter=3, leading=13,
        leftIndent=8, rightIndent=8
    ))
    return styles

S = build_styles()

# ── Helper: download image ────────────────────────────────────────────────────
def get_image_from_url(url, max_width, max_height):
    try:
        r = requests.get(url, timeout=10)
        r.raise_for_status()
        img_data = io.BytesIO(r.content)
        pil_img = PILImage.open(img_data)
        w, h = pil_img.size
        ratio = min(max_width/w, max_height/h)
        new_w, new_h = w*ratio, h*ratio
        img_data.seek(0)
        rl_img = Image(img_data, width=new_w, height=new_h)
        return rl_img
    except Exception as e:
        print(f"Image fetch failed: {e}")
        return None

# ── Header/Footer ─────────────────────────────────────────────────────────────
def header_footer(canvas, doc):
    canvas.saveState()
    W, H = A4
    # Header bar
    canvas.setFillColor(DARK_BLUE)
    canvas.rect(0, H - 1.5*cm, W, 1.5*cm, fill=1, stroke=0)
    canvas.setFillColor(white)
    canvas.setFont("Helvetica-Bold", 9)
    canvas.drawString(1.5*cm, H - 0.95*cm, "LIVER ABSCESS IN PEDIATRIC PATIENT")
    canvas.setFont("Helvetica", 8)
    canvas.drawRightString(W - 1.5*cm, H - 0.95*cm, "Clinical Reference · 5-Year-Old Male")
    # Footer bar
    canvas.setFillColor(MID_BLUE)
    canvas.rect(0, 0, W, 1.0*cm, fill=1, stroke=0)
    canvas.setFillColor(white)
    canvas.setFont("Helvetica", 7.5)
    canvas.drawString(1.5*cm, 0.35*cm, "Sources: Sleisenger & Fordtran's GI Disease · Bailey & Love's Surgery · Current Surgical Therapy 14e · Harrison's 22e")
    canvas.drawRightString(W - 1.5*cm, 0.35*cm, f"Page {doc.page}")
    canvas.restoreState()

# ── Cover page builder ────────────────────────────────────────────────────────
def cover_page():
    elements = []
    W, H = A4

    # Full-bleed cover gradient via canvas is tricky in platypus; use Table approach
    cover_data = [[
        Paragraph("LIVER ABSCESS", S['CoverTitle']),
    ]]
    cover_table = Table([[
        Paragraph("<br/><br/><br/>", S['CoverTitle']),
        Paragraph("LIVER ABSCESS", S['CoverTitle']),
        Paragraph("in a Pediatric Patient", S['CoverSub']),
        Paragraph("Clinical Case: 5-Year-Old Male", S['CoverPatient']),
        Paragraph("Aetiology · Pathogenesis · Diagnosis · Management · Complications", S['CoverSub']),
        Paragraph("<br/>", S['CoverSub']),
    ]], colWidths=[16.5*cm])

    # Use a drawing for the cover
    from reportlab.platypus import Flowable
    class CoverBlock(Flowable):
        def __init__(self):
            Flowable.__init__(self)
            self.width, self.height = A4
        def draw(self):
            c = self.canv
            W, H = A4
            # Background gradient (top dark, bottom medium)
            c.setFillColor(DARK_BLUE)
            c.rect(0, 0, W, H, fill=1, stroke=0)
            # Accent stripe
            c.setFillColor(MID_BLUE)
            c.rect(0, H*0.32, W, H*0.04, fill=1, stroke=0)
            c.setFillColor(ACCENT_ORANGE)
            c.rect(0, H*0.29, W, H*0.03, fill=1, stroke=0)
            # Title
            c.setFillColor(white)
            c.setFont("Helvetica-Bold", 38)
            c.drawCentredString(W/2, H*0.65, "LIVER ABSCESS")
            c.setFont("Helvetica-Bold", 18)
            c.setFillColor(HexColor("#cce0f5"))
            c.drawCentredString(W/2, H*0.57, "in the Pediatric Patient")
            # Patient info box
            c.setFillColor(HexColor("#ffe082"))
            c.roundRect(W/2 - 5*cm, H*0.44, 10*cm, 1.4*cm, 6, fill=1, stroke=0)
            c.setFillColor(DARK_BLUE)
            c.setFont("Helvetica-Bold", 14)
            c.drawCentredString(W/2, H*0.485, "Clinical Case: 5-Year-Old Male")
            # Subtitle
            c.setFillColor(HexColor("#90caf9"))
            c.setFont("Helvetica", 11)
            c.drawCentredString(W/2, H*0.39, "Aetiology  ·  Pathogenesis  ·  Diagnosis  ·  Management  ·  Complications")
            # Bottom line
            c.setFillColor(HexColor("#90caf9"))
            c.setFont("Helvetica", 9)
            c.drawCentredString(W/2, H*0.10, "Based on: Sleisenger & Fordtran's GI Disease | Bailey & Love's Surgery")
            c.drawCentredString(W/2, H*0.07, "Current Surgical Therapy 14e | Harrison's Principles 22e | PubMed Review 2022")
            c.setFont("Helvetica-Oblique", 8)
            c.setFillColor(HexColor("#aaaaaa"))
            c.drawCentredString(W/2, H*0.04, "Prepared with Orris Medical AI · May 2026")

    elements.append(CoverBlock())
    elements.append(PageBreak())
    return elements

# ── Section header helper ─────────────────────────────────────────────────────
def section_header(title, color=DARK_BLUE):
    tbl = Table([[Paragraph(f"  {title}", ParagraphStyle(
        'SH2', fontName='Helvetica-Bold', fontSize=12,
        textColor=white, leading=16
    ))]], colWidths=[16.5*cm])
    tbl.setStyle(TableStyle([
        ('BACKGROUND', (0,0), (-1,-1), color),
        ('TOPPADDING',  (0,0), (-1,-1), 5),
        ('BOTTOMPADDING', (0,0), (-1,-1), 5),
        ('LEFTPADDING', (0,0), (-1,-1), 8),
        ('RIGHTPADDING', (0,0), (-1,-1), 8),
    ]))
    return tbl

def sub_header(title):
    return Paragraph(f"<b><font color='#{DARK_BLUE.hexval()[2:]}'>{title}</font></b>", S['SubHeader'])

def body(text):
    return Paragraph(text, S['Body'])

def bullet(text, symbol="•"):
    return Paragraph(f"{symbol}  {text}", S['BulletBody'])

def spacer(h=0.3):
    return Spacer(1, h*cm)

# ── INFO BOX ─────────────────────────────────────────────────────────────────
def info_box(title, lines, bg=LIGHT_BLUE, title_color=DARK_BLUE, border=MID_BLUE):
    rows = []
    title_row = [Paragraph(f"<b>{title}</b>", ParagraphStyle(
        'IB_T', fontName='Helvetica-Bold', fontSize=10,
        textColor=title_color
    ))]
    rows.append(title_row)
    for line in lines:
        rows.append([Paragraph(line, S['SmallBody'])])
    tbl = Table(rows, colWidths=[16.0*cm])
    tbl.setStyle(TableStyle([
        ('BACKGROUND', (0,0), (-1,0), bg),
        ('BACKGROUND', (0,1), (-1,-1), LIGHT_GRAY),
        ('GRID', (0,0), (-1,-1), 0.3, border),
        ('TOPPADDING',  (0,0), (-1,-1), 4),
        ('BOTTOMPADDING', (0,0), (-1,-1), 4),
        ('LEFTPADDING', (0,0), (-1,-1), 8),
    ]))
    return tbl

# ── WARNING BOX ──────────────────────────────────────────────────────────────
def warning_box(text):
    tbl = Table([[Paragraph(f"⚠  {text}", ParagraphStyle(
        'WB', fontName='Helvetica-Bold', fontSize=9.5,
        textColor=HexColor("#7d3c00")
    ))]], colWidths=[16.0*cm])
    tbl.setStyle(TableStyle([
        ('BACKGROUND', (0,0), (-1,-1), WARN_BG),
        ('BOX', (0,0), (-1,-1), 1.5, WARN_BORDER),
        ('TOPPADDING',  (0,0), (-1,-1), 5),
        ('BOTTOMPADDING', (0,0), (-1,-1), 5),
        ('LEFTPADDING', (0,0), (-1,-1), 10),
    ]))
    return tbl

# ── MAIN CONTENT ──────────────────────────────────────────────────────────────
def build_content():
    e = []

    # ── 1. INTRODUCTION & CASE ────────────────────────────────────────────────
    e.append(section_header("1. INTRODUCTION & CLINICAL CASE", DARK_BLUE))
    e.append(spacer(0.2))

    # Patient case summary box
    case_data = [
        [Paragraph("<b>PATIENT PROFILE</b>", ParagraphStyle('PP', fontName='Helvetica-Bold', fontSize=11, textColor=white))],
        [Paragraph("Age: 5 years  |  Sex: Male  |  Presentation: 5-day history of fever, right upper quadrant abdominal pain, anorexia, malaise", S['SmallBody'])],
        [Paragraph("Examination: Fever 38.9°C, tachycardia, hepatomegaly, tender RUQ on percussion, no jaundice", S['SmallBody'])],
        [Paragraph("Key History: Recent travel to endemic area, no prior biliary disease, no immunosuppression noted", S['SmallBody'])],
    ]
    case_tbl = Table(case_data, colWidths=[16.0*cm])
    case_tbl.setStyle(TableStyle([
        ('BACKGROUND', (0,0), (-1,0), MID_BLUE),
        ('BACKGROUND', (0,1), (-1,-1), LIGHT_BLUE),
        ('GRID', (0,0), (-1,-1), 0.3, MID_BLUE),
        ('TOPPADDING',  (0,0), (-1,-1), 5),
        ('BOTTOMPADDING', (0,0), (-1,-1), 5),
        ('LEFTPADDING', (0,0), (-1,-1), 10),
    ]))
    e.append(case_tbl)
    e.append(spacer(0.3))

    e.append(body(
        "Liver abscess is an uncommon but potentially life-threatening condition in children. It occurs at a rate of approximately "
        "<b>1 per 5,000 hospital admissions</b> but carries significant morbidity if not promptly diagnosed and treated. "
        "Three distinct categories exist: <b>pyogenic (bacterial), amoebic (parasitic), and fungal</b>. In a 5-year-old male, "
        "amoebic liver abscess (ALA) must be considered alongside pyogenic causes, particularly with a travel history. "
        "S. aureus liver abscess — most common in children — and conditions causing immunodeficiency (e.g., chronic granulomatous disease) are important paediatric considerations."
    ))
    e.append(spacer(0.2))

    e.append(warning_box(
        "S. aureus liver abscesses are most common in children and in patients with chronic granulomatous disease or other immunodeficiencies. "
        "Always screen for underlying immunodeficiency in a child with pyogenic liver abscess and no biliary cause."
    ))
    e.append(spacer(0.4))

    # ── 2. CLASSIFICATION ─────────────────────────────────────────────────────
    e.append(section_header("2. CLASSIFICATION OF LIVER ABSCESS", DARK_BLUE))
    e.append(spacer(0.2))

    class_data = [
        [Paragraph("Type", S['TableHeader']), Paragraph("Causative Agent", S['TableHeader']),
         Paragraph("Paediatric Relevance", S['TableHeader']), Paragraph("Key Features", S['TableHeader'])],
        [Paragraph("Pyogenic", S['TableCell']), Paragraph("E. coli, Klebsiella, S. aureus, Streptococcus milleri, Bacteroides, anaerobes", S['TableCell']),
         Paragraph("⭐ Most common in children (especially S. aureus)", S['TableCell']),
         Paragraph("Often polymicrobial; biliary/portal/haematogenous route", S['TableCell'])],
        [Paragraph("Amoebic", S['TableCell']), Paragraph("Entamoeba histolytica", S['TableCell']),
         Paragraph("Rare in children; consider after travel to endemic area", S['TableCell']),
         Paragraph("Monomicrobial; chocolate-brown 'anchovy paste' pus", S['TableCell'])],
        [Paragraph("Fungal", S['TableCell']), Paragraph("Candida spp., Aspergillus", S['TableCell']),
         Paragraph("Immunocompromised children (haematologic malignancy)", S['TableCell']),
         Paragraph("Highest mortality; multiple microabscesses", S['TableCell'])],
    ]
    class_tbl = Table(class_data, colWidths=[2.5*cm, 4.5*cm, 5.0*cm, 4.5*cm])
    class_tbl.setStyle(TableStyle([
        ('BACKGROUND', (0,0), (-1,0), DARK_BLUE),
        ('BACKGROUND', (0,1), (-1,1), LIGHT_BLUE),
        ('BACKGROUND', (0,2), (-1,2), LIGHT_GRAY),
        ('BACKGROUND', (0,3), (-1,3), LIGHT_BLUE),
        ('GRID', (0,0), (-1,-1), 0.5, MED_GRAY),
        ('TOPPADDING',  (0,0), (-1,-1), 5),
        ('BOTTOMPADDING', (0,0), (-1,-1), 5),
        ('LEFTPADDING', (0,0), (-1,-1), 6),
        ('ALIGN', (0,0), (-1,0), 'CENTER'),
        ('VALIGN', (0,0), (-1,-1), 'TOP'),
    ]))
    e.append(class_tbl)
    e.append(spacer(0.4))

    # ── 3. PATHOGENESIS ───────────────────────────────────────────────────────
    e.append(section_header("3. PATHOGENESIS & AETIOLOGY", MID_BLUE))
    e.append(spacer(0.2))

    e.append(sub_header("Routes of Infection"))
    routes = [
        ("<b>Biliary route (35%):</b> Cholangitis, cholecystitis → most common identifiable source overall. Less common in children than adults."),
        ("<b>Portal venous route (20%):</b> Ascending infection from appendicitis, omphalitis (neonates), pylephlebitis — historically the main paediatric cause."),
        ("<b>Haematogenous/Systemic (hepatic artery):</b> S. aureus bacteraemia, infective endocarditis, septicaemia — important in children."),
        ("<b>Direct extension:</b> Subphrenic abscess, penetrating trauma, infected foreign body."),
        ("<b>Cryptogenic (~40%):</b> No identifiable source; often Klebsiella pneumoniae; in children consider dental/periodontal sources."),
        ("<b>Amoebic (E. histolytica):</b> Trophozoites invade colonic mucosa → portal blood → liver. Leads to single large right lobe abscess with 'anchovy paste' aspirate."),
    ]
    for r in routes:
        e.append(bullet(r))
    e.append(spacer(0.3))

    e.append(sub_header("Paediatric-Specific Predisposing Factors"))
    ped_factors = [
        "Chronic granulomatous disease (CGD) — defective NADPH oxidase → recurrent catalase-positive infections (S. aureus, Klebsiella)",
        "Immunosuppression: leukaemia, post-transplant, HIV",
        "Omphalitis / umbilical vein catheterisation in neonates",
        "Complicated appendicitis with portal pyaemia",
        "Sickle cell disease, malnutrition",
        "Travel to or immigration from endemic areas (amoebiasis)",
    ]
    for f in ped_factors:
        e.append(bullet(f))
    e.append(spacer(0.4))

    # ── 4. MICROBIOLOGY ───────────────────────────────────────────────────────
    e.append(section_header("4. MICROBIOLOGY", DARK_BLUE))
    e.append(spacer(0.2))
    e.append(body(
        "Pyogenic liver abscesses are usually polymicrobial. In children, <b>Staphylococcus aureus</b> is the most common single organism. "
        "Gram-negative enteric organisms predominate in biliary/portal-source abscesses."
    ))
    e.append(spacer(0.15))

    micro_data = [
        [Paragraph("Gram-Negative Aerobic", S['TableHeader']), Paragraph("Gram-Positive Aerobic", S['TableHeader']),
         Paragraph("Anaerobes", S['TableHeader']), Paragraph("Other", S['TableHeader'])],
        [Paragraph("Escherichia coli\nKlebsiella pneumoniae\nEnterobacter spp.\nPseudomonas spp.\nProteus spp.", S['SmallBody']),
         Paragraph("Staphylococcus aureus ★\nStreptococcus milleri group\nStreptococcus pyogenes\nEnterococcus spp.", S['SmallBody']),
         Paragraph("Bacteroides fragilis\nFusobacterium spp.\nAnaerobic streptococci\nClostridium spp.", S['SmallBody']),
         Paragraph("Entamoeba histolytica\nCandida spp.\nMycobacterium tuberculosis\n(rare, immunocomp.)", S['SmallBody'])],
    ]
    micro_tbl = Table(micro_data, colWidths=[4.0*cm, 4.0*cm, 4.0*cm, 4.5*cm])
    micro_tbl.setStyle(TableStyle([
        ('BACKGROUND', (0,0), (-1,0), MID_BLUE),
        ('BACKGROUND', (0,1), (-1,-1), LIGHT_GRAY),
        ('GRID', (0,0), (-1,-1), 0.5, MED_GRAY),
        ('TOPPADDING',  (0,0), (-1,-1), 4),
        ('BOTTOMPADDING', (0,0), (-1,-1), 4),
        ('LEFTPADDING', (0,0), (-1,-1), 5),
        ('VALIGN', (0,0), (-1,-1), 'TOP'),
    ]))
    e.append(micro_tbl)
    e.append(spacer(0.15))
    e.append(Paragraph("★ S. aureus: most common cause of liver abscess in children; also predominant organism in chronic granulomatous disease.", S['SmallBody']))
    e.append(spacer(0.4))

    # ── 5. CLINICAL FEATURES ──────────────────────────────────────────────────
    e.append(section_header("5. CLINICAL FEATURES", MID_BLUE))
    e.append(spacer(0.2))

    signs_data = [
        [Paragraph("Symptom/Sign", S['TableHeader']), Paragraph("Frequency", S['TableHeader']),
         Paragraph("Notes", S['TableHeader'])],
        [Paragraph("Fever (often spiking)", S['TableCell']), Paragraph("~90%", S['TableCell']),
         Paragraph("May be low-grade or insidious onset in children", S['TableCell'])],
        [Paragraph("Abdominal pain (RUQ)", S['TableCell']), Paragraph("~60%", S['TableCell']),
         Paragraph("Worse with movement; referred to right shoulder if near dome", S['TableCell'])],
        [Paragraph("Hepatomegaly + tenderness", S['TableCell']), Paragraph("Common", S['TableCell']),
         Paragraph("Accentuated by percussion (fist percussion sign)", S['TableCell'])],
        [Paragraph("Anorexia, malaise, weight loss", S['TableCell']), Paragraph("Common", S['TableCell']),
         Paragraph("May be the predominant presentation; often precedes diagnosis by weeks", S['TableCell'])],
        [Paragraph("Nausea / vomiting", S['TableCell']), Paragraph("Variable", S['TableCell']),
         Paragraph("More common in amoebic ALA", S['TableCell'])],
        [Paragraph("Jaundice", S['TableCell']), Paragraph("<25%", S['TableCell']),
         Paragraph("Late sign; absent without cholangitis; suggests biliary source", S['TableCell'])],
        [Paragraph("Cough / dyspnoea", S['TableCell']), Paragraph("Variable", S['TableCell']),
         Paragraph("Diaphragmatic irritation; may suggest pleuropulmonary involvement", S['TableCell'])],
        [Paragraph("Diarrhoea (bloody)", S['TableCell']), Paragraph("Rare in pyogenic", S['TableCell']),
         Paragraph("Concurrent amoebic dysentery suggests ALA", S['TableCell'])],
    ]
    signs_tbl = Table(signs_data, colWidths=[4.5*cm, 2.5*cm, 9.5*cm])
    signs_tbl.setStyle(TableStyle([
        ('BACKGROUND', (0,0), (-1,0), DARK_BLUE),
        ('ROWBACKGROUNDS', (0,1), (-1,-1), [white, LIGHT_GRAY]),
        ('GRID', (0,0), (-1,-1), 0.5, MED_GRAY),
        ('TOPPADDING',  (0,0), (-1,-1), 4),
        ('BOTTOMPADDING', (0,0), (-1,-1), 4),
        ('LEFTPADDING', (0,0), (-1,-1), 6),
        ('VALIGN', (0,0), (-1,-1), 'TOP'),
    ]))
    e.append(signs_tbl)
    e.append(spacer(0.4))

    # ── 6. INVESTIGATIONS ─────────────────────────────────────────────────────
    e.append(section_header("6. INVESTIGATIONS", DARK_BLUE))
    e.append(spacer(0.2))
    e.append(sub_header("Laboratory Studies"))
    labs = [
        "<b>FBC:</b> Leukocytosis (~90% of cases); anaemia (chronic disease)",
        "<b>LFTs:</b> Elevated ALP (~80%); elevated bilirubin (~50%); transaminitis (~50%)",
        "<b>CRP/ESR:</b> Elevated; monitor response to treatment",
        "<b>Blood cultures:</b> Positive in >50% of pyogenic cases — essential before antibiotics",
        "<b>Serology:</b> Amoebic serology (IHA, ELISA) — high sensitivity/specificity for ALA; useful in children",
        "<b>Stool microscopy &amp; culture:</b> Cysts/trophozoites in ~50% of ALA — send stool O&P",
        "<b>Procalcitonin:</b> Elevated; helps differentiate bacterial from other causes",
        "<b>Immunological workup:</b> DHR/NBT test to exclude CGD if recurrent or unusual organisms",
    ]
    for l in labs:
        e.append(bullet(l))
    e.append(spacer(0.3))

    e.append(sub_header("Imaging"))
    e.append(spacer(0.15))
    imaging_data = [
        [Paragraph("Modality", S['TableHeader']), Paragraph("Sensitivity", S['TableHeader']),
         Paragraph("Paediatric Preference", S['TableHeader']), Paragraph("Key Findings", S['TableHeader'])],
        [Paragraph("Ultrasound (US)", S['TableCell']), Paragraph("75–90%", S['TableCell']),
         Paragraph("⭐ FIRST-LINE in children (no radiation)", S['TableCell']),
         Paragraph("Hypoechoic/heterogeneous mass; internal echoes; can guide aspiration", S['TableCell'])],
        [Paragraph("CT (with IV contrast)", S['TableCell']), Paragraph("~100%", S['TableCell']),
         Paragraph("If US inconclusive; surgical planning", S['TableCell']),
         Paragraph("Hypodense lesion; peripheral rim enhancement (<20%); gas = bad sign; loculations", S['TableCell'])],
        [Paragraph("MRI", S['TableCell']), Paragraph("High", S['TableCell']),
         Paragraph("Small abscesses; no radiation", S['TableCell']),
         Paragraph("T1 low SI, T2 high SI; gadolinium enhances rim; superior for small lesions", S['TableCell'])],
        [Paragraph("CXR", S['TableCell']), Paragraph("Low", S['TableCell']),
         Paragraph("Initial screen", S['TableCell']),
         Paragraph("Elevated R hemi-diaphragm; basal atelectasis; R pleural effusion", S['TableCell'])],
    ]
    img_tbl = Table(imaging_data, colWidths=[2.8*cm, 2.2*cm, 4.0*cm, 7.5*cm])
    img_tbl.setStyle(TableStyle([
        ('BACKGROUND', (0,0), (-1,0), MID_BLUE),
        ('ROWBACKGROUNDS', (0,1), (-1,-1), [white, LIGHT_BLUE]),
        ('GRID', (0,0), (-1,-1), 0.5, MED_GRAY),
        ('TOPPADDING',  (0,0), (-1,-1), 4),
        ('BOTTOMPADDING', (0,0), (-1,-1), 4),
        ('LEFTPADDING', (0,0), (-1,-1), 5),
        ('VALIGN', (0,0), (-1,-1), 'TOP'),
    ]))
    e.append(img_tbl)
    e.append(spacer(0.4))

    # Images from textbooks/search
    e.append(sub_header("Imaging Gallery"))
    e.append(spacer(0.15))

    img_urls = [
        ("https://cdn.orris.care/cdss_images/ebae9395b11200b4a01df42abd7bc211f945ea4280987d316d8341d60a5acaf2.png",
         "Fig. 1: CT scan showing liver abscess with rim enhancement (Current Surgical Therapy 14e, p.440)"),
        ("https://cdn.orris.care/cdss_images/pmc_clinical_VQA_3dd86f2681fae8c08e5ca532d6f8ca61d584447826be6c3d4a287343cc8207fe.jpg",
         "Fig. 2: CT – Pediatric pyogenic liver abscess (left lobe) with rim enhancement and extension to falciform ligament"),
        ("https://cdn.orris.care/cdss_images/a2e4d1eb80dab7d2a77764277a685f9db881e6c15b11e0bfd8d3ba744bc95665.png",
         "Fig. 3: Ultrasound – Liver abscess (Bailey & Love's Surgery 28e)"),
        ("https://cdn.orris.care/cdss_images/pmc_clinical_VQA_47510f064ff2f15ba9c569f0d46a9322100b4291a902ef1ca6a91a60e261ec7b.jpg",
         "Fig. 4: Serial imaging showing resolution of liver abscess (US at 1 month and 3 months; CT at 8 months post-treatment)"),
    ]

    image_rows = []
    for url, cap in img_urls:
        img = get_image_from_url(url, 7.5*cm, 5.5*cm)
        if img:
            image_rows.append([img, Paragraph(cap, S['Caption'])])

    if len(image_rows) >= 2:
        for i in range(0, len(image_rows), 2):
            row1_img = image_rows[i][0]
            row1_cap = image_rows[i][1]
            if i+1 < len(image_rows):
                row2_img = image_rows[i+1][0]
                row2_cap = image_rows[i+1][1]
            else:
                row2_img = Spacer(1, 0.1*cm)
                row2_cap = Paragraph("", S['Caption'])
            img_grid = Table(
                [[row1_img, row2_img], [row1_cap, row2_cap]],
                colWidths=[8.0*cm, 8.0*cm]
            )
            img_grid.setStyle(TableStyle([
                ('ALIGN', (0,0), (-1,-1), 'CENTER'),
                ('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
                ('TOPPADDING', (0,0), (-1,-1), 4),
                ('BOTTOMPADDING', (0,0), (-1,-1), 4),
            ]))
            e.append(img_grid)
            e.append(spacer(0.2))

    e.append(spacer(0.4))

    # ── 7. DIAGNOSIS ──────────────────────────────────────────────────────────
    e.append(section_header("7. DIFFERENTIAL DIAGNOSIS", MID_BLUE))
    e.append(spacer(0.2))
    diffs = [
        "<b>Hepatocellular carcinoma / hepatoblastoma:</b> Hepatic mass; AFP elevated",
        "<b>Hepatic cysts:</b> Simple cysts / hydatid (Echinococcus) — thin-walled, no fever",
        "<b>Haemangioma:</b> Vascular pattern on US/CT; no fever",
        "<b>Focal nodular hyperplasia / adenoma:</b> Young patients; incidental finding",
        "<b>Necrotic/post-chemotherapy hepatic lesion:</b> History of malignancy",
        "<b>Hepatic haematoma:</b> History of trauma",
        "<b>Inflammatory pseudotumour of liver:</b> Rare benign; young men; simulates abscess/tumour on imaging",
        "<b>Biliary pathology (cholangitis, cholecystitis):</b> Biliary dilation on imaging",
        "<b>Sub-phrenic abscess:</b> Elevated diaphragm; may mimic liver lesion",
    ]
    for d in diffs:
        e.append(bullet(d))
    e.append(spacer(0.4))

    # ── 8. MANAGEMENT ─────────────────────────────────────────────────────────
    e.append(section_header("8. MANAGEMENT", DARK_BLUE))
    e.append(spacer(0.2))

    e.append(sub_header("A. Pyogenic Liver Abscess — Treatment Algorithm"))
    e.append(spacer(0.1))
    e.append(body(
        "Treatment requires: (1) identification and treatment of the source, (2) microbiological diagnosis via blood and abscess cultures, "
        "(3) appropriate antibiotic therapy, and (4) drainage of the abscess in most cases."
    ))
    e.append(spacer(0.15))

    mgmt_data = [
        [Paragraph("Step", S['TableHeader']), Paragraph("Intervention", S['TableHeader']),
         Paragraph("Details", S['TableHeader'])],
        [Paragraph("1", S['TableCell']), Paragraph("Blood cultures + abscess aspiration", S['TableCell']),
         Paragraph("Obtain before starting antibiotics. Send aerobic + anaerobic cultures. Positive in >50% of cases.", S['TableCell'])],
        [Paragraph("2", S['TableCell']), Paragraph("Empiric IV antibiotics", S['TableCell']),
         Paragraph("Piperacillin/tazobactam (monotherapy) OR ceftriaxone + metronidazole. Consider carbapenems if ESBL risk/post-biliary instrumentation. Cover anaerobes (metronidazole/clindamycin) and Gram-negatives.", S['TableCell'])],
        [Paragraph("3", S['TableCell']), Paragraph("Percutaneous drainage (US/CT-guided)", S['TableCell']),
         Paragraph("Standard of care. Catheter drainage recommended for abscesses >5 cm. Aspiration alone for <3 cm if technically feasible. Multiple catheters for >10 cm abscesses.", S['TableCell'])],
        [Paragraph("4", S['TableCell']), Paragraph("Target antibiotics on culture results", S['TableCell']),
         Paragraph("IV antibiotics until clinical response achieved, then switch to PO for total 4–6 weeks (guided by imaging follow-up at 4–6 weeks).", S['TableCell'])],
        [Paragraph("5", S['TableCell']), Paragraph("Treat underlying cause", S['TableCell']),
         Paragraph("Biliary decompression if biliary origin (ERCP/sphincterotomy/PTD). Treat appendicitis, omphalitis, etc.", S['TableCell'])],
        [Paragraph("6", S['TableCell']), Paragraph("Surgical drainage (if percutaneous fails)", S['TableCell']),
         Paragraph("Incomplete percutaneous drainage, multiloculated abscess, ruptured abscess, unresolved jaundice, renal impairment. Laparoscopic approach preferred.", S['TableCell'])],
    ]
    mgmt_tbl = Table(mgmt_data, colWidths=[1.2*cm, 4.3*cm, 11.0*cm])
    mgmt_tbl.setStyle(TableStyle([
        ('BACKGROUND', (0,0), (-1,0), DARK_BLUE),
        ('ROWBACKGROUNDS', (0,1), (-1,-1), [white, LIGHT_BLUE]),
        ('GRID', (0,0), (-1,-1), 0.5, MED_GRAY),
        ('TOPPADDING',  (0,0), (-1,-1), 5),
        ('BOTTOMPADDING', (0,0), (-1,-1), 5),
        ('LEFTPADDING', (0,0), (-1,-1), 5),
        ('VALIGN', (0,0), (-1,-1), 'TOP'),
        ('ALIGN', (0,0), (0,-1), 'CENTER'),
    ]))
    e.append(mgmt_tbl)
    e.append(spacer(0.3))

    e.append(sub_header("B. Antibiotic Regimens (Paediatric Dosing)"))
    e.append(spacer(0.1))
    abx_data = [
        [Paragraph("Scenario", S['TableHeader']), Paragraph("Preferred Regimen", S['TableHeader']),
         Paragraph("Alternative", S['TableHeader'])],
        [Paragraph("Empiric (all-source, pyogenic)", S['TableCell']),
         Paragraph("Piperacillin/tazobactam\n90 mg/kg/dose IV q6–8h", S['TableCell']),
         Paragraph("Ceftriaxone 50 mg/kg IV OD + Metronidazole 7.5 mg/kg IV q8h", S['TableCell'])],
        [Paragraph("Biliary source / ESBL risk", S['TableCell']),
         Paragraph("Meropenem 20 mg/kg IV q8h", S['TableCell']),
         Paragraph("Imipenem-cilastatin + vancomycin (if MRSA risk)", S['TableCell'])],
        [Paragraph("S. aureus (CGD/paediatric)", S['TableCell']),
         Paragraph("Flucloxacillin 50 mg/kg IV q6h (MSSA)\nVancomycin 15 mg/kg IV q6h (MRSA)", S['TableCell']),
         Paragraph("Clindamycin (if sensitive)", S['TableCell'])],
        [Paragraph("Amoebic (ALA)", S['TableCell']),
         Paragraph("Metronidazole 35–50 mg/kg/day PO/IV in 3 divided doses × 7–10 days", S['TableCell']),
         Paragraph("Tinidazole 50 mg/kg PO OD × 5 days (≥3 years)", S['TableCell'])],
        [Paragraph("Luminal eradication (post-ALA)", S['TableCell']),
         Paragraph("Paromomycin 25–35 mg/kg/day PO in 3 doses × 7 days", S['TableCell']),
         Paragraph("Diloxanide furoate (not available in all countries)", S['TableCell'])],
    ]
    abx_tbl = Table(abx_data, colWidths=[3.5*cm, 6.5*cm, 6.5*cm])
    abx_tbl.setStyle(TableStyle([
        ('BACKGROUND', (0,0), (-1,0), MID_BLUE),
        ('ROWBACKGROUNDS', (0,1), (-1,-1), [white, LIGHT_GRAY]),
        ('GRID', (0,0), (-1,-1), 0.5, MED_GRAY),
        ('TOPPADDING',  (0,0), (-1,-1), 4),
        ('BOTTOMPADDING', (0,0), (-1,-1), 4),
        ('LEFTPADDING', (0,0), (-1,-1), 5),
        ('VALIGN', (0,0), (-1,-1), 'TOP'),
    ]))
    e.append(abx_tbl)
    e.append(spacer(0.15))
    e.append(warning_box(
        "Always start metronidazole BEFORE aspiration if amoebic liver abscess is suspected — aspiration of ALA without metronidazole cover risks spreading infection. "
        "ALA drainage is generally NOT required unless failure of medical therapy, left lobe ALA (risk of pericardial rupture), or large abscess at risk of rupture."
    ))
    e.append(spacer(0.4))

    # ── 9. COMPLICATIONS ──────────────────────────────────────────────────────
    e.append(section_header("9. COMPLICATIONS", DARK_BLUE))
    e.append(spacer(0.2))
    comp_data = [
        [Paragraph("Complication", S['TableHeader']), Paragraph("Notes", S['TableHeader'])],
        [Paragraph("Rupture → peritonitis", S['TableCell']),
         Paragraph("Most feared complication; higher risk with abscesses >10 cm; requires emergency surgical drainage", S['TableCell'])],
        [Paragraph("Pleuropulmonary disease", S['TableCell']),
         Paragraph("~20% of ALA; empyema, hepatobronchial fistula, lung abscess; right pleural effusion (10–15%)", S['TableCell'])],
        [Paragraph("Pericardial extension", S['TableCell']),
         Paragraph("Left lobe ALA — pericardial effusion/tamponade; rare but life-threatening", S['TableCell'])],
        [Paragraph("Portal/splenic vein thrombosis", S['TableCell']),
         Paragraph("Post-infective portal hypertension may follow", S['TableCell'])],
        [Paragraph("Septic emboli / bacteraemia", S['TableCell']),
         Paragraph("Metastatic endophthalmitis (Klebsiella, up to 10% diabetics); brain abscess", S['TableCell'])],
        [Paragraph("Recurrent abscess (12–38%)", S['TableCell']),
         Paragraph("When source not identified or treated; more common with diabetes and unknown organism", S['TableCell'])],
        [Paragraph("Mortality", S['TableCell']),
         Paragraph("4–10% overall in modern series; worse with delay in diagnosis, multiple abscesses, sepsis, malignancy, fungal cause", S['TableCell'])],
    ]
    comp_tbl = Table(comp_data, colWidths=[4.5*cm, 12.0*cm])
    comp_tbl.setStyle(TableStyle([
        ('BACKGROUND', (0,0), (-1,0), ACCENT_RED),
        ('ROWBACKGROUNDS', (0,1), (-1,-1), [HexColor("#fff5f5"), LIGHT_GRAY]),
        ('GRID', (0,0), (-1,-1), 0.5, MED_GRAY),
        ('TOPPADDING',  (0,0), (-1,-1), 5),
        ('BOTTOMPADDING', (0,0), (-1,-1), 5),
        ('LEFTPADDING', (0,0), (-1,-1), 6),
        ('VALIGN', (0,0), (-1,-1), 'TOP'),
    ]))
    e.append(comp_tbl)
    e.append(spacer(0.4))

    # ── 10. AMOEBIC LIVER ABSCESS IN CHILDREN ────────────────────────────────
    e.append(section_header("10. AMOEBIC LIVER ABSCESS (ALA) IN CHILDREN", MID_BLUE))
    e.append(spacer(0.2))
    e.append(body(
        "Although amoebic liver abscess is predominantly a disease of adult males (10:1 male:female), it must be considered in children "
        "with <b>travel history</b> to endemic areas (South Asia, Mexico, West/South Africa). ALA is the most common form of "
        "extraintestinal amebiasis and is caused by E. histolytica trophozoites invading the portal circulation."
    ))
    e.append(spacer(0.15))
    ala_points = [
        "<b>Presentation:</b> Fever, RUQ pain, tender hepatomegaly. Concurrent diarrhoea only in 50% — absence does not exclude.",
        "<b>Aspirate:</b> Chocolate-brown 'anchovy paste' — sterile on bacterial culture (unless secondary infection).",
        "<b>Serology:</b> Anti-amoeba antibodies (IHA/ELISA) — high sensitivity; may be negative in early acute illness.",
        "<b>Stool O&P:</b> Positive in ~50% of cases with ALA.",
        "<b>Imaging:</b> Single large abscess in right lobe (>80%); hypoechoic on US; homogeneous low-density on CT.",
        "<b>Treatment:</b> Metronidazole (or tinidazole) is curative in >95%. Follow with luminal cysticidal agent (paromomycin).",
        "<b>Drainage:</b> Not routinely required. Reserve for: left lobe (pericardial risk), large abscess >10 cm, no improvement after 3–5 days of metronidazole, secondary infection.",
        "<b>Reference (PMID 34794678):</b> Gupta et al., Pediatr Clin North Am 2022 — comprehensive review on amebiasis and ALA in children.",
    ]
    for a in ala_points:
        e.append(bullet(a))
    e.append(spacer(0.4))

    # ── 11. FOLLOW-UP ─────────────────────────────────────────────────────────
    e.append(section_header("11. FOLLOW-UP & PROGNOSIS", DARK_BLUE))
    e.append(spacer(0.2))
    fu_points = [
        "Repeat imaging (US or CT) at <b>4–6 weeks</b> after treatment to confirm resolution.",
        "Duration of antibiotics guided by clinical response and imaging — typically <b>4–6 weeks total</b> (IV until response, then oral).",
        "Drainage catheters remain until output is minimal and clear for several days.",
        "Monitor for biliary communication (bile in drains → HIDA scan / MRCP / ERCP).",
        "<b>Investigate for underlying immunodeficiency</b> in all children with pyogenic liver abscess, especially if caused by S. aureus or atypical organisms — DHR assay to exclude CGD.",
        "ALA: follow-up imaging may show persistent cavity for months even after clinical cure — do not over-interpret.",
        "Recurrence prevention: treat source (biliary, appendicitis); complete luminal cysticidal therapy for ALA.",
        "Mortality has dramatically improved with modern antibiotics and percutaneous drainage; overall 4–10%.",
    ]
    for f in fu_points:
        e.append(bullet(f))
    e.append(spacer(0.4))

    # ── 12. KEY POINTS SUMMARY ────────────────────────────────────────────────
    e.append(section_header("12. KEY CLINICAL PEARLS", MID_BLUE))
    e.append(spacer(0.2))
    pearls = [
        ("In a 5-year-old male with fever + RUQ pain, liver abscess must be on the differential. US is the first-line imaging modality.", DARK_BLUE),
        ("S. aureus is the most common cause of pyogenic liver abscess in children — always screen for chronic granulomatous disease.", ACCENT_RED),
        ("Amoebic liver abscess is rare in children but critical to consider with travel history. Start metronidazole before aspiration.", ACCENT_ORANGE),
        ("Blood cultures before antibiotics — positive in >50% of pyogenic cases.", MID_BLUE),
        ("Percutaneous catheter drainage is standard of care for abscesses >5 cm. Surgery reserved for failures.", GREEN),
        ("Empiric therapy: pip-tazo monotherapy OR ceftriaxone + metronidazole. Duration: 4–6 weeks total.", DARK_BLUE),
        ("Investigate all paediatric liver abscess for underlying immunodeficiency (DHR test / NBT for CGD).", ACCENT_RED),
        ("ALA drainage is not routinely required — most respond to metronidazole alone. Always follow with luminal cysticidal agent.", ACCENT_ORANGE),
    ]
    for i, (pearl, col) in enumerate(pearls, 1):
        row_bg = LIGHT_BLUE if i % 2 == 0 else white
        p_tbl = Table([[
            Paragraph(f"<b>{i}</b>", ParagraphStyle('PNum', fontName='Helvetica-Bold', fontSize=12,
                                                      textColor=white, alignment=TA_CENTER)),
            Paragraph(pearl, S['SmallBody'])
        ]], colWidths=[1.0*cm, 15.0*cm])
        p_tbl.setStyle(TableStyle([
            ('BACKGROUND', (0,0), (0,-1), col),
            ('BACKGROUND', (1,0), (1,-1), row_bg),
            ('TOPPADDING', (0,0), (-1,-1), 5),
            ('BOTTOMPADDING', (0,0), (-1,-1), 5),
            ('LEFTPADDING', (0,0), (-1,-1), 6),
            ('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
        ]))
        e.append(p_tbl)
        e.append(spacer(0.05))
    e.append(spacer(0.4))

    # ── 13. REFERENCES ────────────────────────────────────────────────────────
    e.append(section_header("13. REFERENCES", DARK_BLUE))
    e.append(spacer(0.15))
    refs = [
        "1. Feldman M, et al. <i>Sleisenger and Fordtran's Gastrointestinal and Liver Disease</i>, 10e. Chapter 84: Liver Abscess (pp. 1591–1598).",
        "2. Williams NS, et al. <i>Bailey and Love's Short Practice of Surgery</i>, 28e. Chapter 69: Liver abscess (pp. 2667–2720).",
        "3. Cameron JL (ed). <i>Current Surgical Therapy</i>, 14e. Pyogenic and Amebic Liver Abscess (pp. 440–447).",
        "4. Kasper DL, et al. <i>Harrison's Principles of Internal Medicine</i>, 22e (2025). Amebiasis: Amebic Liver Abscess.",
        "5. Gupta S, Smith L, Diakiw A. Amebiasis and Amebic Liver Abscess in Children. <i>Pediatr Clin North Am</i>. 2022;69(1):69–85. PMID: 34794678.",
        "6. Murray & Nadel's Textbook of Respiratory Medicine: Pleuropulmonary complications of amebic liver abscess.",
        "7. Goodman & Gilman's Pharmacological Basis of Therapeutics, 13e: Metronidazole, paromomycin — antiparasitic agents.",
    ]
    for ref in refs:
        e.append(Paragraph(ref, S['SmallBody']))
        e.append(spacer(0.1))

    return e

# ── BUILD PDF ──────────────────────────────────────────────────────────────────
def main():
    doc = SimpleDocTemplate(
        OUTPUT_PATH,
        pagesize=A4,
        leftMargin=2.0*cm,
        rightMargin=2.0*cm,
        topMargin=2.2*cm,
        bottomMargin=1.8*cm,
        title="Liver Abscess in a 5-Year-Old Male",
        author="Orris Medical AI",
        subject="Paediatric Liver Abscess — Clinical Reference",
    )

    story = []
    story.extend(cover_page())
    story.extend(build_content())

    doc.build(story, onFirstPage=lambda c, d: None, onLaterPages=header_footer)
    print(f"PDF saved to: {OUTPUT_PATH}")

if __name__ == "__main__":
    main()
Running Command

cd /home/daytona/workspace/liver-abscess-pdf && python generate_pdf.py 2>&1

The CoverBlock custom flowable needs to handle the frame size properly. Let me fix this:
Editing File

~/liver-abscess-pdf/generate_pdf.py

Successfully replaced 1 instance(s) of the string in '/home/daytona/workspace/liver-abscess-pdf/generate_pdf.py'
Your credits are exhausted. Your daily credits reset tomorrow, or you can purchase a top-up for more.
This is a shared conversation. Sign in to Orris to start your own chat.