Add text rendering with auto-fit support
text() renders text into a rectangle with alignment (left/center/right). Pass size=0 to auto-size the text to fill the rectangle. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
51
spork.py
51
spork.py
@@ -1,6 +1,6 @@
|
||||
from reportlab.lib.units import inch
|
||||
from reportlab.pdfgen import canvas
|
||||
from PIL import Image, ImageDraw
|
||||
from PIL import Image, ImageDraw, ImageFont
|
||||
import os
|
||||
import tempfile
|
||||
|
||||
@@ -95,6 +95,32 @@ def ReadJPG(filepath):
|
||||
|
||||
DPI = 300
|
||||
|
||||
# Alignment
|
||||
LEFT = "left"
|
||||
CENTER = "center"
|
||||
RIGHT = "right"
|
||||
|
||||
|
||||
def _render_text(page, text, font_path, size_pt, x, y, w, h, align, color):
|
||||
size_px = int(size_pt * DPI / 72)
|
||||
font = ImageFont.truetype(font_path, size_px)
|
||||
draw = ImageDraw.Draw(page)
|
||||
bbox = draw.textbbox((0, 0), text, font=font)
|
||||
tw = bbox[2] - bbox[0]
|
||||
th = bbox[3] - bbox[1]
|
||||
xp = int(x * DPI)
|
||||
yp = int(y * DPI)
|
||||
wp = int(w * DPI)
|
||||
hp = int(h * DPI)
|
||||
if align == CENTER:
|
||||
tx = xp + (wp - tw) // 2
|
||||
elif align == RIGHT:
|
||||
tx = xp + wp - tw
|
||||
else:
|
||||
tx = xp
|
||||
ty = yp + (hp - th) // 2
|
||||
draw.text((tx, ty - bbox[1]), text, font=font, fill=color + (255,))
|
||||
|
||||
|
||||
class NewPDF:
|
||||
def __init__(self, width_inches, height_inches):
|
||||
@@ -116,6 +142,9 @@ class NewPDF:
|
||||
def line(self, color, x0, y0, x1, y1, width=1):
|
||||
self._ops.append(("line", color, x0, y0, x1, y1, width))
|
||||
|
||||
def text(self, string, font, size, x, y, w, h, align=LEFT, color=BLACK):
|
||||
self._ops.append(("text", string, font, size, x, y, w, h, align, color))
|
||||
|
||||
def save(self, filename):
|
||||
pw = int(self.width_inches * DPI)
|
||||
ph = int(self.height_inches * DPI)
|
||||
@@ -152,6 +181,26 @@ class NewPDF:
|
||||
fill=color + (255,), width=width,
|
||||
)
|
||||
|
||||
elif op[0] == "text":
|
||||
_, string, font, size, x, y, w, h, align, color = op
|
||||
if size == 0:
|
||||
wp = int(w * DPI)
|
||||
hp = int(h * DPI)
|
||||
lo, hi = 1, 1000
|
||||
while lo < hi:
|
||||
mid = (lo + hi + 1) // 2
|
||||
size_px = int(mid * DPI / 72)
|
||||
f = ImageFont.truetype(font, size_px)
|
||||
bbox = ImageDraw.Draw(page).textbbox((0, 0), string, font=f)
|
||||
tw = bbox[2] - bbox[0]
|
||||
th = bbox[3] - bbox[1]
|
||||
if tw <= wp and th <= hp:
|
||||
lo = mid
|
||||
else:
|
||||
hi = mid - 1
|
||||
size = lo
|
||||
_render_text(page, string, font, size, x, y, w, h, align, color)
|
||||
|
||||
# Write composited image to PDF
|
||||
rgb_page = page.convert("RGB")
|
||||
c = canvas.Canvas(filename, pagesize=(self.width, self.height))
|
||||
|
||||
Reference in New Issue
Block a user