Add scripts/fill_flat_pdf.py (v1.0.0)
This commit is contained in:
@@ -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()
|
||||
Reference in New Issue
Block a user