You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

generate_pdf.py 4.3 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. import unicodedata
  2. import uuid
  3. from io import BytesIO
  4. from pathlib import Path
  5. from typing import Dict, List, Optional
  6. import requests
  7. from autogen_core.code_executor import ImportFromModule
  8. from autogen_core.tools import FunctionTool
  9. from fpdf import FPDF
  10. from PIL import Image, ImageDraw, ImageOps
  11. async def generate_pdf(
  12. sections: List[Dict[str, Optional[str]]], output_file: str = "report.pdf", report_title: str = "PDF Report"
  13. ) -> str:
  14. """
  15. Generate a PDF report with formatted sections including text and images.
  16. Args:
  17. sections: List of dictionaries containing section details with keys:
  18. - title: Section title
  19. - level: Heading level (title, h1, h2)
  20. - content: Section text content
  21. - image: Optional image URL or file path
  22. output_file: Name of output PDF file
  23. report_title: Title shown at top of report
  24. Returns:
  25. str: Path to the generated PDF file
  26. """
  27. def normalize_text(text: str) -> str:
  28. """Normalize Unicode text to ASCII."""
  29. return unicodedata.normalize("NFKD", text).encode("ascii", "ignore").decode("ascii")
  30. def get_image(image_url_or_path):
  31. """Fetch image from URL or local path."""
  32. if image_url_or_path.startswith(("http://", "https://")):
  33. response = requests.get(image_url_or_path)
  34. if response.status_code == 200:
  35. return BytesIO(response.content)
  36. elif Path(image_url_or_path).is_file():
  37. return open(image_url_or_path, "rb")
  38. return None
  39. def add_rounded_corners(img, radius=6):
  40. """Add rounded corners to an image."""
  41. mask = Image.new("L", img.size, 0)
  42. draw = ImageDraw.Draw(mask)
  43. draw.rounded_rectangle([(0, 0), img.size], radius, fill=255)
  44. img = ImageOps.fit(img, mask.size, centering=(0.5, 0.5))
  45. img.putalpha(mask)
  46. return img
  47. class PDF(FPDF):
  48. """Custom PDF class with header and content formatting."""
  49. def header(self):
  50. self.set_font("Arial", "B", 12)
  51. normalized_title = normalize_text(report_title)
  52. self.cell(0, 10, normalized_title, 0, 1, "C")
  53. def chapter_title(self, txt):
  54. self.set_font("Arial", "B", 12)
  55. normalized_txt = normalize_text(txt)
  56. self.cell(0, 10, normalized_txt, 0, 1, "L")
  57. self.ln(2)
  58. def chapter_body(self, body):
  59. self.set_font("Arial", "", 12)
  60. normalized_body = normalize_text(body)
  61. self.multi_cell(0, 10, normalized_body)
  62. self.ln()
  63. def add_image(self, img_data):
  64. img = Image.open(img_data)
  65. img = add_rounded_corners(img)
  66. img_path = Path(f"temp_{uuid.uuid4().hex}.png")
  67. img.save(img_path, format="PNG")
  68. self.image(str(img_path), x=None, y=None, w=190 if img.width > 190 else img.width)
  69. self.ln(10)
  70. img_path.unlink()
  71. # Initialize PDF
  72. pdf = PDF()
  73. pdf.add_page()
  74. font_size = {"title": 16, "h1": 14, "h2": 12, "body": 12}
  75. # Add sections
  76. for section in sections:
  77. title = section.get("title", "")
  78. level = section.get("level", "h1")
  79. content = section.get("content", "")
  80. image = section.get("image")
  81. pdf.set_font("Arial", "B" if level in font_size else "", font_size.get(level, font_size["body"]))
  82. pdf.chapter_title(title)
  83. if content:
  84. pdf.chapter_body(content)
  85. if image:
  86. img_data = get_image(image)
  87. if img_data:
  88. pdf.add_image(img_data)
  89. if isinstance(img_data, BytesIO):
  90. img_data.close()
  91. pdf.output(output_file)
  92. return output_file
  93. # Create the PDF generation tool
  94. generate_pdf_tool = FunctionTool(
  95. func=generate_pdf,
  96. description="Generate PDF reports with formatted sections containing text and images",
  97. global_imports=[
  98. "uuid",
  99. "requests",
  100. "unicodedata",
  101. ImportFromModule("typing", ("List", "Dict", "Optional")),
  102. ImportFromModule("pathlib", ("Path",)),
  103. ImportFromModule("fpdf", ("FPDF",)),
  104. ImportFromModule("PIL", ("Image", "ImageDraw", "ImageOps")),
  105. ImportFromModule("io", ("BytesIO",)),
  106. ],
  107. )