Add scripts/fill_flat_pdf.py (v1.0.0)

This commit is contained in:
2026-07-20 15:00:27 -05:00
parent 6e665f40d5
commit 24c32614b2
+100
View File
@@ -0,0 +1,100 @@
#!/usr/bin/env python3
"""
fill_flat_pdf.py — overlay text onto a flat (non-fillable) PDF.
For forms that look like forms but have no AcroForm fields. You supply the
placements (page index, x, y in PDF points from the bottom-left, and the text);
this stamps them onto a copy of the PDF.
Usage:
python fill_flat_pdf.py INPUT.pdf OUTPUT.pdf PLACEMENTS.json
# or pass placements inline:
python fill_flat_pdf.py INPUT.pdf OUTPUT.pdf '[{"page":0,"x":150,"y":690,"text":"Message Point Media of Alabama Inc."}]'
PLACEMENTS.json format:
[
{"page": 0, "x": 150, "y": 690, "text": "Message Point Media of Alabama Inc.", "size": 10},
{"page": 0, "x": 150, "y": 668, "text": "4628 Montevallo Rd, Suite 204"}
]
Coordinates: PDF origin is bottom-left; y increases upward; 72 points = 1 inch.
If your source coordinates are top-left, convert with y_pdf = page_height - y_top.
Use --list-sizes to print each page's width/height in points to help you place text.
Requires: pypdf and reportlab (pip install pypdf reportlab --break-system-packages)
"""
import sys
import json
import io
def page_sizes(input_pdf):
from pypdf import PdfReader
reader = PdfReader(input_pdf)
for i, page in enumerate(reader.pages):
box = page.mediabox
print(f"page {i}: width={float(box.width):.1f} height={float(box.height):.1f} (points)")
def fill(input_pdf, output_pdf, placements):
from pypdf import PdfReader, PdfWriter
from reportlab.pdfgen import canvas
reader = PdfReader(input_pdf)
writer = PdfWriter()
# group placements by page
by_page = {}
for p in placements:
by_page.setdefault(int(p.get("page", 0)), []).append(p)
for i, page in enumerate(reader.pages):
if i in by_page:
box = page.mediabox
w, h = float(box.width), float(box.height)
buf = io.BytesIO()
c = canvas.Canvas(buf, pagesize=(w, h))
for p in by_page[i]:
size = float(p.get("size", 10))
c.setFont(p.get("font", "Helvetica"), size)
c.drawString(float(p["x"]), float(p["y"]), str(p["text"]))
c.save()
buf.seek(0)
overlay = PdfReader(buf).pages[0]
page.merge_page(overlay)
writer.add_page(page)
with open(output_pdf, "wb") as f:
writer.write(f)
print(f"Wrote {output_pdf} with {sum(len(v) for v in by_page.values())} text placements.")
def main():
args = sys.argv[1:]
if args and args[0] == "--list-sizes":
if len(args) != 2:
print("Usage: python fill_flat_pdf.py --list-sizes INPUT.pdf")
sys.exit(1)
page_sizes(args[1])
return
if len(args) != 3:
print(__doc__)
sys.exit(1)
input_pdf, output_pdf, placements_arg = args
try:
placements = json.loads(placements_arg)
except json.JSONDecodeError:
with open(placements_arg) as f:
placements = json.load(f)
if not isinstance(placements, list):
print("Placements must be a JSON array of {page,x,y,text} objects.")
sys.exit(1)
fill(input_pdf, output_pdf, placements)
if __name__ == "__main__":
main()