Files
Tracker/Code/Tools/Samples/generators/pdf_scanned_letter.py
T
2026-02-21 21:55:42 +00:00

145 lines
3.7 KiB
Python

"""Generate PDF: Scanned letter FROM client (simulated scan effect)."""
import io
from pathlib import Path
from reportlab.lib.pagesizes import A4
from reportlab.lib.units import mm
from reportlab.pdfgen import canvas
from reportlab.lib.colors import HexColor
try:
from PIL import Image, ImageFilter, ImageEnhance
import random as _random
HAS_PIL = True
except ImportError:
HAS_PIL = False
def _create_clean_pdf(
buffer, from_name, from_company, from_address, to_name, to_firm, body, date
):
"""Create a clean PDF letter."""
c = canvas.Canvas(buffer, pagesize=A4)
w, h = A4
# Company header
c.setFont("Helvetica-Bold", 14)
c.drawString(50, h - 60, from_company)
c.setFont("Helvetica", 9)
y = h - 78
for line in from_address.split(","):
c.drawString(50, y, line.strip())
y -= 14
# Date
y -= 20
c.setFont("Helvetica", 11)
c.drawString(50, y, date)
# Recipient
y -= 30
c.drawString(50, y, f"Mr. {to_name}")
y -= 16
c.drawString(50, y, to_firm)
y -= 16
c.drawString(50, y, "Suites 3203-3207, 32/F, Edinburgh Tower, The Landmark")
y -= 16
c.drawString(50, y, "15 Queen's Road Central, Central, Hong Kong")
# Body
y -= 35
c.setFont("Helvetica", 11)
for para in body.split("\n\n"):
for line_text in _wrap_text(para.strip(), 85):
if y < 80:
c.showPage()
y = h - 60
c.setFont("Helvetica", 11)
c.drawString(50, y, line_text)
y -= 16
y -= 10
# Signature area
y -= 20
c.drawString(50, y, "Yours sincerely,")
y -= 40
# Simulate a signature squiggle
c.setStrokeColor(HexColor("#1a1a8a"))
c.setLineWidth(1.5)
import random
random.seed(hash(from_name))
sx = 50
sy = y + 10
c.line(sx, sy, sx + 30, sy + 8)
c.line(sx + 30, sy + 8, sx + 50, sy - 5)
c.line(sx + 50, sy - 5, sx + 80, sy + 3)
c.setStrokeColor(HexColor("#000000"))
y -= 10
c.setFont("Helvetica-Bold", 11)
c.drawString(50, y, from_name)
y -= 16
c.setFont("Helvetica", 10)
c.drawString(50, y, from_company)
c.save()
def _wrap_text(text, max_chars):
"""Simple word-wrap."""
words = text.split()
lines = []
current = ""
for word in words:
if len(current) + len(word) + 1 > max_chars:
lines.append(current)
current = word
else:
current = f"{current} {word}" if current else word
if current:
lines.append(current)
return lines
def generate(
output_path: Path,
from_name: str,
from_company: str,
from_address: str,
to_name: str,
to_firm: str,
body: str,
date: str,
):
"""Generate a scanned-looking PDF letter."""
# First create a clean PDF in memory
buf = io.BytesIO()
_create_clean_pdf(
buf, from_name, from_company, from_address, to_name, to_firm, body, date
)
buf.seek(0)
if HAS_PIL:
# Convert to image, degrade slightly, save back as PDF
try:
from pdf2image import convert_from_bytes
images = convert_from_bytes(buf.read(), dpi=150)
if images:
img = images[0]
# Add slight grey tint (paper simulation)
enhancer = ImageEnhance.Brightness(img)
img = enhancer.enhance(0.95)
# Slight blur for scan effect
img = img.filter(ImageFilter.GaussianBlur(radius=0.3))
# Save as PDF
img.save(str(output_path), "PDF", resolution=150)
return
except Exception:
pass
# Fallback: save clean PDF as-is
output_path.write_bytes(buf.getvalue())