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.lib.units import inch
|
||||||
from reportlab.pdfgen import canvas
|
from reportlab.pdfgen import canvas
|
||||||
from PIL import Image, ImageDraw
|
from PIL import Image, ImageDraw, ImageFont
|
||||||
import os
|
import os
|
||||||
import tempfile
|
import tempfile
|
||||||
|
|
||||||
@@ -95,6 +95,32 @@ def ReadJPG(filepath):
|
|||||||
|
|
||||||
DPI = 300
|
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:
|
class NewPDF:
|
||||||
def __init__(self, width_inches, height_inches):
|
def __init__(self, width_inches, height_inches):
|
||||||
@@ -116,6 +142,9 @@ class NewPDF:
|
|||||||
def line(self, color, x0, y0, x1, y1, width=1):
|
def line(self, color, x0, y0, x1, y1, width=1):
|
||||||
self._ops.append(("line", color, x0, y0, x1, y1, width))
|
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):
|
def save(self, filename):
|
||||||
pw = int(self.width_inches * DPI)
|
pw = int(self.width_inches * DPI)
|
||||||
ph = int(self.height_inches * DPI)
|
ph = int(self.height_inches * DPI)
|
||||||
@@ -152,6 +181,26 @@ class NewPDF:
|
|||||||
fill=color + (255,), width=width,
|
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
|
# Write composited image to PDF
|
||||||
rgb_page = page.convert("RGB")
|
rgb_page = page.convert("RGB")
|
||||||
c = canvas.Canvas(filename, pagesize=(self.width, self.height))
|
c = canvas.Canvas(filename, pagesize=(self.width, self.height))
|
||||||
|
|||||||
Reference in New Issue
Block a user