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:
2026-05-04 16:41:20 -04:00
parent ea5b20b3ce
commit db3b3c2558

View File

@@ -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))