Button guide

An internal PDF merge and split tool for your office, built from a Python script no public PDF website needed

You can give your office an internal PDF tool by putting a pypdf script on one of your own computers and turning it into a shared web form with Button (https://button.bitgate.dev). Your colleagues upload up to 8 files per run (up to 25 MB each, 100 MB total) in their browser, the script merges, splits or stamps them on your machine, and they download the finished PDFs without installing anything. The Free plan is $0 for 1 tool, 3 people and 100 runs per month, and Pro is $15/month (or $144/year, which comes to $12/month) for 50 tools and 250 people.

Write the pypdf script

Before you hook a script up to Button, it needs to take all of its inputs as command-line arguments rather than hard-coded paths or interactive prompts. In Python, argparse is the straightforward way to do that. You take the input file paths and an output folder path as arguments, and you write the finished PDFs into that output folder.

One detail about how Button runs your program: the runner passes every form value to your program as its own argument, with no shell, and a user's form value can't start with -. Each file input on the form holds one file and becomes one argument, with up to 8 file inputs per run, so for a batch of PDFs it's simplest to accept one ZIP. Fixed flags like --action or --output-dir are part of the base command you approve on your machine, while the values your colleagues fill into the form are passed in right after those flags.

Here's a Python script using pypdf that merges or splits PDFs and can stamp the first page of a watermark PDF onto every page. It takes one PDF or a ZIP of PDFs (merged in filename order), and a split comes back as one ZIP of single-page files:

import argparse
import os
import tempfile
import zipfile
from pypdf import PdfReader, PdfWriter

parser = argparse.ArgumentParser()
parser.add_argument("--action", choices=["merge", "split"], required=True)
parser.add_argument("--input", required=True)
parser.add_argument("--output-dir", required=True)
parser.add_argument("--stamp-file", default="")
args = parser.parse_args()

stamp_page = None
if args.stamp_file and os.path.isfile(args.stamp_file):
    stamp_page = PdfReader(args.stamp_file).pages[0]


def stamped(page):
    if stamp_page:
        page.merge_page(stamp_page)
    return page


with tempfile.TemporaryDirectory() as workdir:
    pdfs = [args.input]
    if args.input.lower().endswith(".zip"):
        with zipfile.ZipFile(args.input) as archive:
            archive.extractall(workdir)
        pdfs = []
        for root, _, names in os.walk(workdir):
            pdfs += [os.path.join(root, n) for n in names if n.lower().endswith(".pdf")]
        pdfs.sort(key=os.path.basename)

    if args.action == "merge":
        writer = PdfWriter()
        for path in pdfs:
            for page in PdfReader(path).pages:
                writer.add_page(stamped(page))
        writer.write(os.path.join(args.output_dir, "merged.pdf"))
    else:
        zip_path = os.path.join(args.output_dir, "pages.zip")
        with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as pages_zip:
            for path in pdfs:
                base = os.path.splitext(os.path.basename(path))[0]
                for number, page in enumerate(PdfReader(path).pages, start=1):
                    single = PdfWriter()
                    single.add_page(stamped(page))
                    page_path = os.path.join(workdir, f"{base}_page_{number}.pdf")
                    single.write(page_path)
                    pages_zip.write(page_path, arcname=os.path.basename(page_path))

Save that on your computer as pdf_tool.py and make sure pypdf is installed in the Python environment you'll point Button at. (If you'd rather write this in Node, R, PowerShell, a shell script or a compiled binary, any program works the same way: take inputs with commandArgs() in R, a param() block in PowerShell, or $1 and $2 in bash.)

Publish the script as a web form in Button

Once the script runs from your terminal, setting it up for the office takes a few minutes.

  1. Sign in at https://button.bitgate.dev.
  2. Install the Button runner on your computer (macOS, Linux or Windows) using the one-line command the site shows you - it's a curl command on macOS and Linux, and PowerShell on Windows.
  3. Open the local setup page and pick your pdf_tool.py program.
  4. Describe the inputs for the form. Button supports text, number, yes/no, choice and files inputs. For this tool, set up a choice input for merge or split and a file input for the PDF or ZIP. For a stamp, keep --stamp-file pointed at your company stamp PDF as part of the command you approve, or leave it out.
  5. Publish the tool. Button gives the tool its own page and pins the exact command you approved on your computer by hash.
  6. Invite the colleagues who need it.

When colleagues get their invite email, they open the link and sign in with Google or a one-time email code. They don't install anything and they never pay. They upload a PDF or a ZIP of PDFs, choose merge or split, press Run, and download the result when the job finishes.

If you later edit the command or update the tool configuration, the runner won't just run the new version silently; people have to confirm the new version before running it. You can also check the run log to see who ran the tool and when, and you can revoke someone's access or set it to expire at any time.

How files move, limits, and when to use a desktop app instead

The PDF jobs execute on your computer, never in Button's cloud. However, form values and files do pass through Button's servers on the way between your colleague's browser and your computer. Input files are deleted 24 hours after a run ends, and output files are kept for 7 days so the person who ran the job has time to download them.

There are also hard limits on file sizes and concurrency that matter for PDF work. Each computer runs one job at a time, and your computer has to be switched on and online for the tool to work (the tool page shows Ready, Busy or Offline so people aren't left guessing). Every plan allows one computer (runner) per workspace; pairing a new computer replaces the previous one.

Here is how the limits and plans break down:

Limit or plan item Free ($0) Pro ($15/month or $144/year)
Published tools 1 50
Invited people (you don't count) 3 250
Runs per month (includes your own runs) 100 5,000
File transfer per month 1 GB 50 GB
Max files per run 8 files 8 files
Max size per file 25 MB 25 MB
Max total size per run 100 MB 100 MB
Max run duration 30 minutes 30 minutes
Max log output per run 1 MB 1 MB
Input file deletion 24 hours after run ends 24 hours after run ends
Output file retention 7 days 7 days

Because of the 25 MB per-file limit, the 100 MB per-run cap, and the fact that files are uploaded and downloaded over the network through Button's servers, Button isn't the right fit for every PDF job. If your office regularly works with very large scans, architectural drawings, or multi-hundred-megabyte document bundles, a local desktop PDF app installed on each person's computer is the better choice. You'll also want a local desktop app if your policy forbids files from passing through an external relay server at all.

For everyday contracts, invoices and forms under 25 MB where you just want staff off random public PDF websites, a shared script on an office machine covers it. And if you start on Pro and later drop back to Free, downgrading deletes nothing.

FAQ

Do the PDF files stay exclusively on my computer?

No. The script runs on your computer, not in Button's cloud, but the form values and files pass through Button's servers to get from your colleague's browser to your machine and back. Button deletes input files 24 hours after a run finishes and deletes output files after 7 days.

Can a colleague pass malicious flags through the form to run other commands?

No. The runner only executes the exact command you approved on your computer, pinned by hash, and passes every form value as a separate argument without a shell. Form values also can't start with -, so a colleague can't inject new command-line flags.

What happens if my computer is asleep or running someone else's PDF job?

The tool page shows your computer's status as Ready, Busy or Offline. A computer processes one run at a time (up to 30 minutes per run), and if your machine is turned off or offline, colleagues won't be able to run the tool until it's back online.

Do my colleagues need a paid seat or a Python install?

Recipients never pay and never install anything. They sign in with Google or a one-time email code from their invite email and run the form in their browser; on your side, the "people" limit on Free (3) and Pro (250) only counts the distinct colleagues you've invited, not you.

Last updated

Put your script behind a form

Button turns a script on your computer into a web form. Colleagues sign in, fill it in, press Run and download the results. Free for 1 tool and 3 people.