mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-07 08:26:10 +00:00
225 lines
8.9 KiB
Python
225 lines
8.9 KiB
Python
from __future__ import annotations
|
|
|
|
import base64
|
|
from functools import cache
|
|
from io import BytesIO
|
|
from typing import Final
|
|
from urllib.parse import quote
|
|
|
|
from PIL import Image, ImageDraw
|
|
from reportlab.graphics.barcode import code128 # pyright: ignore[reportMissingTypeStubs] # ReportLab has no stubs
|
|
from reportlab.lib import colors # pyright: ignore[reportMissingTypeStubs] # ReportLab has no stubs
|
|
from reportlab.lib.pagesizes import letter # pyright: ignore[reportMissingTypeStubs] # ReportLab has no stubs
|
|
from reportlab.lib.utils import ImageReader # pyright: ignore[reportMissingTypeStubs] # ReportLab has no stubs
|
|
from reportlab.pdfgen import canvas # pyright: ignore[reportMissingTypeStubs] # ReportLab has no stubs
|
|
|
|
|
|
def dummy_image_url(text: str, font_size: int, width: int = 800, height: int = 300) -> str:
|
|
return f"https://dummyjson.com/image/{width}x{height}/ffffff/000000?text={quote(text)}&fontSize={font_size}"
|
|
|
|
|
|
_GLYPHS: Final = {
|
|
"D": ("11110", "10001", "10001", "10001", "10001", "10001", "11110"),
|
|
"O": ("01110", "10001", "10001", "10001", "10001", "10001", "01110"),
|
|
"C": ("01111", "10000", "10000", "10000", "10000", "10000", "01111"),
|
|
"1": ("00100", "01100", "00100", "00100", "00100", "00100", "01110"),
|
|
"2": ("01110", "10001", "00001", "00010", "00100", "01000", "11111"),
|
|
"3": ("11110", "00001", "00001", "01110", "00001", "00001", "11110"),
|
|
}
|
|
|
|
|
|
@cache
|
|
def structured_image_bytes() -> bytes:
|
|
image: Final = Image.new("RGB", (320, 80), "white")
|
|
draw: Final = ImageDraw.Draw(image)
|
|
scale: Final = 8
|
|
cursor_x = 24
|
|
for character in "DOC 123":
|
|
if character == " ":
|
|
cursor_x += scale * 3
|
|
continue
|
|
for glyph_y, row in enumerate(_GLYPHS[character]):
|
|
for glyph_x, filled in enumerate(row):
|
|
if filled == "1":
|
|
x = cursor_x + glyph_x * scale
|
|
y = 12 + glyph_y * scale
|
|
draw.rectangle((x, y, x + scale - 1, y + scale - 1), fill="black")
|
|
cursor_x += scale * 6
|
|
output: Final = BytesIO()
|
|
image.save(output, format="PNG")
|
|
return output.getvalue()
|
|
|
|
|
|
@cache
|
|
def structured_image_data_uri() -> str:
|
|
encoded: Final = base64.b64encode(structured_image_bytes()).decode("ascii")
|
|
return f"data:image/png;base64,{encoded}"
|
|
|
|
|
|
def _draw_header(pdf: canvas.Canvas, title: str, page_number: int) -> None:
|
|
pdf.setFillColor(colors.black)
|
|
pdf.setFont("Helvetica", 11)
|
|
pdf.drawString(45, 770, "Quarterly Operations Report")
|
|
pdf.setFont("Helvetica-Bold", 16)
|
|
pdf.drawString(45, 745, title)
|
|
pdf.setFont("Helvetica", 9)
|
|
pdf.drawString(45, 30, f"Confidential | Page {page_number} of 5")
|
|
|
|
|
|
def _draw_body(pdf: canvas.Canvas, page_number: int) -> None:
|
|
pdf.setFont("Helvetica", 10)
|
|
for line_number in range(1, 9):
|
|
pdf.drawString(
|
|
45,
|
|
500 - (line_number * 28),
|
|
f"Section {page_number}.{line_number}: Invoice totals, regional revenue, and reconciliation notes.",
|
|
)
|
|
|
|
|
|
def _diagram_image(width: int, height: int, accent: tuple[int, int, int]) -> Image.Image:
|
|
image: Final = Image.new("RGB", (width, height), (242, 246, 252))
|
|
draw: Final = ImageDraw.Draw(image)
|
|
for coordinate in range(0, max(width, height), 40):
|
|
draw.line((coordinate, 0, coordinate, height), fill=(32, 32, 32), width=3)
|
|
draw.line((0, coordinate, width, coordinate), fill=(32, 32, 32), width=3)
|
|
draw.line((0, 0, width, height), fill=accent, width=8)
|
|
draw.line((width, 0, 0, height), fill=accent, width=8)
|
|
draw.rectangle((width // 4, height // 4, width * 3 // 4, height * 3 // 4), outline=accent, width=6)
|
|
return image
|
|
|
|
|
|
def _draw_embedded_images(pdf: canvas.Canvas) -> None:
|
|
images: Final = (
|
|
(_diagram_image(320, 320, (51, 115, 217)), 455, 655, 70, 70),
|
|
(_diagram_image(360, 320, (38, 151, 92)), 455, 565, 70, 62),
|
|
(_diagram_image(120, 120, (219, 68, 55)), 455, 500, 45, 45),
|
|
)
|
|
for image, x, y, width, height in images:
|
|
pdf.drawImage( # pyright: ignore[reportUnknownMemberType] # ReportLab has no stubs
|
|
ImageReader(image), x, y, width=width, height=height, mask="auto"
|
|
)
|
|
|
|
|
|
def _draw_table_page(pdf: canvas.Canvas) -> None:
|
|
columns: Final = (45, 245, 405, 565)
|
|
tables: Final = (
|
|
(
|
|
(730, 695, 660, 625),
|
|
(
|
|
("Item", "Quantity", "Amount", 707),
|
|
("Document analysis", "2", "120.00", 672),
|
|
("Document verification", "1", "80.00", 637),
|
|
),
|
|
),
|
|
(
|
|
(600, 565, 530, 495),
|
|
(
|
|
("Item continued", "Quantity", "Amount", 577),
|
|
("Fixture validation", "3", "45.00", 542),
|
|
("Provider review", "1", "25.00", 507),
|
|
),
|
|
),
|
|
)
|
|
for rows, values in tables:
|
|
for x in columns:
|
|
pdf.line(x, rows[-1], x, rows[0])
|
|
for y in rows:
|
|
pdf.line(45, y, 565, y)
|
|
for item, quantity, amount, y in values:
|
|
pdf.drawString(55, y, item)
|
|
pdf.drawString(255, y, quantity)
|
|
pdf.drawString(415, y, amount)
|
|
|
|
|
|
def _draw_chart_page(pdf: canvas.Canvas) -> None:
|
|
bars: Final = ((70, 70), (170, 115), (270, 90), (370, 130))
|
|
pdf.setFillColor(colors.HexColor("#3373D9"))
|
|
for x, height in bars:
|
|
pdf.rect(x, 610, 65, height, fill=1, stroke=0)
|
|
pdf.setFillColor(colors.black)
|
|
for quarter, x in zip(("Q1", "Q2", "Q3", "Q4"), (90, 190, 290, 390), strict=True):
|
|
pdf.drawString(x, 590, quarter)
|
|
pdf.drawString(45, 550, "Formula: gross margin = (revenue - cost) / revenue")
|
|
_draw_embedded_images(pdf)
|
|
|
|
|
|
def _draw_metadata_page(pdf: canvas.Canvas) -> None:
|
|
pdf.setFont("Helvetica", 12)
|
|
pdf.drawString(45, 700, "Invoice Number: INV-2048")
|
|
pdf.drawString(45, 675, "Purchase Order: PO-4096")
|
|
pdf.setFillColor(colors.HexColor("#F2E65A"))
|
|
pdf.rect(40, 555, 500, 24, fill=1, stroke=0)
|
|
pdf.setFillColor(colors.black)
|
|
pdf.drawString(45, 560, "Highlighted total requiring review")
|
|
pdf.drawString(45, 530, "Reviewer comment: verify the highlighted total before approval")
|
|
pdf.setFillColor(colors.red)
|
|
pdf.drawString(45, 495, "Revised total: 245.00")
|
|
pdf.line(45, 501, 150, 501)
|
|
pdf.setFillColor(colors.black)
|
|
pdf.linkURL( # pyright: ignore[reportUnknownMemberType] # ReportLab has no stubs
|
|
"https://example.com/invoices/INV-2048", (45, 575, 300, 590), relative=0
|
|
)
|
|
pdf.highlightAnnotation( # pyright: ignore[reportUnknownMemberType] # ReportLab has no stubs
|
|
"Total highlighted for review",
|
|
Rect=(40, 555, 540, 579),
|
|
QuadPoints=(40, 579, 540, 579, 40, 555, 540, 555),
|
|
)
|
|
pdf.textAnnotation( # pyright: ignore[reportUnknownMemberType] # ReportLab has no stubs
|
|
"Verify the highlighted total", Rect=(520, 525, 540, 545)
|
|
)
|
|
pdf.drawString(45, 575, "https://example.com/invoices/INV-2048")
|
|
barcode: Final = code128.Code128("5901234123457", barHeight=70, barWidth=1.2)
|
|
barcode.drawOn(pdf, 90, 130)
|
|
|
|
|
|
def _draw_signature_page(pdf: canvas.Canvas) -> None:
|
|
pdf.saveState()
|
|
pdf.setFillColor(colors.lightgrey)
|
|
pdf.setFont("Helvetica-Bold", 54)
|
|
pdf.translate(110, 390)
|
|
pdf.rotate(25)
|
|
pdf.drawString(0, 0, "DRAFT")
|
|
pdf.restoreState()
|
|
pdf.setFillColor(colors.black)
|
|
pdf.setFont("Helvetica", 12)
|
|
pdf.drawString(45, 635, "Approved by: Jordan Lee")
|
|
pdf.line(45, 610, 310, 610)
|
|
pdf.bezier(55, 595, 75, 625, 112, 602, 155, 600)
|
|
pdf.drawString(45, 580, "Signature")
|
|
|
|
|
|
def _draw_appendix_page(pdf: canvas.Canvas) -> None:
|
|
pdf.setFont("Helvetica-Bold", 14)
|
|
pdf.drawString(45, 700, "1. Scope")
|
|
pdf.drawString(45, 650, "2. Findings")
|
|
pdf.drawString(45, 600, "3. Recommendations")
|
|
|
|
|
|
@cache
|
|
def structured_pdf_bytes() -> bytes:
|
|
output: Final = BytesIO()
|
|
pdf: Final = canvas.Canvas(output, pagesize=letter, pageCompression=0, invariant=1)
|
|
pdf.setTitle("Quarterly Operations Report")
|
|
pdf.setAuthor("LiteLLM parity fixture generator")
|
|
pdf.setSubject("Semantic document coverage for tables, figures, annotations, and metadata")
|
|
pdf.setKeywords("document, invoice, table, figure, annotation")
|
|
pages: Final = (
|
|
("Invoice Summary and Line Items", _draw_table_page),
|
|
("Revenue Chart and Formula Review", _draw_chart_page),
|
|
("Key Values, Link, Highlight, and Comment", _draw_metadata_page),
|
|
("Approval Signature and Watermark", _draw_signature_page),
|
|
("Appendix with Section Boundaries", _draw_appendix_page),
|
|
)
|
|
for page_number, (title, draw_page) in enumerate(pages, start=1):
|
|
_draw_header(pdf, title, page_number)
|
|
draw_page(pdf)
|
|
_draw_body(pdf, page_number)
|
|
pdf.showPage()
|
|
pdf.save()
|
|
return output.getvalue()
|
|
|
|
|
|
@cache
|
|
def structured_pdf_data_uri() -> str:
|
|
encoded: Final = base64.b64encode(structured_pdf_bytes()).decode("ascii")
|
|
return f"data:application/pdf;base64,{encoded}"
|