129 lines
4.2 KiB
Python
Raw Normal View History

import unicodedata
import uuid
from io import BytesIO
from pathlib import Path
from typing import Dict, List, Optional
import requests
from autogen_core.code_executor import ImportFromModule
from autogen_core.tools import FunctionTool
from fpdf import FPDF
from PIL import Image, ImageDraw, ImageOps
async def generate_pdf(
sections: List[Dict[str, Optional[str]]], output_file: str = "report.pdf", report_title: str = "PDF Report"
) -> str:
"""
Generate a PDF report with formatted sections including text and images.
Args:
sections: List of dictionaries containing section details with keys:
- title: Section title
- level: Heading level (title, h1, h2)
- content: Section text content
- image: Optional image URL or file path
output_file: Name of output PDF file
report_title: Title shown at top of report
Returns:
str: Path to the generated PDF file
"""
def normalize_text(text: str) -> str:
"""Normalize Unicode text to ASCII."""
return unicodedata.normalize("NFKD", text).encode("ascii", "ignore").decode("ascii")
def get_image(image_url_or_path):
"""Fetch image from URL or local path."""
if image_url_or_path.startswith(("http://", "https://")):
response = requests.get(image_url_or_path)
if response.status_code == 200:
return BytesIO(response.content)
elif Path(image_url_or_path).is_file():
return open(image_url_or_path, "rb")
return None
def add_rounded_corners(img, radius=6):
"""Add rounded corners to an image."""
mask = Image.new("L", img.size, 0)
draw = ImageDraw.Draw(mask)
draw.rounded_rectangle([(0, 0), img.size], radius, fill=255)
img = ImageOps.fit(img, mask.size, centering=(0.5, 0.5))
img.putalpha(mask)
return img
class PDF(FPDF):
"""Custom PDF class with header and content formatting."""
def header(self):
self.set_font("Arial", "B", 12)
normalized_title = normalize_text(report_title)
self.cell(0, 10, normalized_title, 0, 1, "C")
def chapter_title(self, txt):
self.set_font("Arial", "B", 12)
normalized_txt = normalize_text(txt)
self.cell(0, 10, normalized_txt, 0, 1, "L")
self.ln(2)
def chapter_body(self, body):
self.set_font("Arial", "", 12)
normalized_body = normalize_text(body)
self.multi_cell(0, 10, normalized_body)
self.ln()
def add_image(self, img_data):
img = Image.open(img_data)
img = add_rounded_corners(img)
img_path = Path(f"temp_{uuid.uuid4().hex}.png")
img.save(img_path, format="PNG")
self.image(str(img_path), x=None, y=None, w=190 if img.width > 190 else img.width)
self.ln(10)
img_path.unlink()
# Initialize PDF
pdf = PDF()
pdf.add_page()
font_size = {"title": 16, "h1": 14, "h2": 12, "body": 12}
# Add sections
for section in sections:
title = section.get("title", "")
level = section.get("level", "h1")
content = section.get("content", "")
image = section.get("image")
pdf.set_font("Arial", "B" if level in font_size else "", font_size.get(level, font_size["body"]))
pdf.chapter_title(title)
if content:
pdf.chapter_body(content)
if image:
img_data = get_image(image)
if img_data:
pdf.add_image(img_data)
if isinstance(img_data, BytesIO):
img_data.close()
pdf.output(output_file)
return output_file
# Create the PDF generation tool
generate_pdf_tool = FunctionTool(
func=generate_pdf,
description="Generate PDF reports with formatted sections containing text and images",
global_imports=[
"uuid",
"requests",
"unicodedata",
ImportFromModule("typing", ("List", "Dict", "Optional")),
ImportFromModule("pathlib", ("Path",)),
ImportFromModule("fpdf", ("FPDF",)),
ImportFromModule("PIL", ("Image", "ImageDraw", "ImageOps")),
ImportFromModule("io", ("BytesIO",)),
],
)