Fill scanned paper forms from a template. Upload a blank form image, draw boxes where text or marks should go, save it as a reusable template, then generate filled copies one at a time or in bulk from an Excel sheet. Arabic (right to left, properly shaped) and English work in the same box.
- Visual editor: draw field boxes directly on your form image.
- Field types:
- Text (Arabic or English, direction detected automatically)
- Date (one box with separate day / month / year slots, so you fill printed cells without a hardcoded separator)
- Multiple choice (places a mark on the selected option; styles: x, check, circle, dot)
- Number cells (one digit per box, for ID or phone numbers)
- Per box font and color pickers, so you can see and change what each box uses.
- 3D depth: drag a box's corner handles to tilt the text into the page's plane (perspective warp), for forms that were photographed or scanned at an angle.
- Multi page templates: one template can hold several base images. Each row of your sheet generates that row's pages in sequence.
- Reusable named templates, tied to the form image by content hash.
- Bulk generation from an .xlsx: one row per form, output named by a serial column or by row number.
- Runs as a desktop window or in the browser.
- Python 3.10 or newer
pip install -r requirements.txt- At least one font file in
workspace/fonts/(any .ttf or .otf). Drop the file in and it shows up in the per box font dropdown. Theworkspace/folder is created automatically on first run. - To make filled forms look hand written rather than typed, use an Arabic handwriting font (a flowing ruqaa or script face) rather than a print naskh. Check that it includes Arabic-Indic digits if you fill numbers with it, since some display and demo fonts ship letters only. Handwriting faces are usually more compact than print ones, so expect to raise the font size by roughly a sixth to keep the same visual height.
Desktop window:
python run.py
Or in a browser:
python -m formforge.server
# then open http://localhost:8000
- Upload your blank form image (Base img). It belongs to the current page.
- Switch to Define boxes, pick a box type, and drag rectangles on the form.
- Date: place the day, month, and year slots.
- Multiple choice: place a mark box for each option and pick the mark style.
- Number cells: set how many cells.
- Set each box's font and color from its dropdowns.
- For depth, press 3D on a box, then drag its corner handles on the form until the text sits in the plane of the paper. Press flat to undo it.
- For a multi page form, press + in the Pages bar. The new page starts with the same box places as page 1 (values cleared), so a second copy of the same form needs no redrawing: just upload that page's base image. Boxes that keep their name share one spreadsheet column and get the same value on both pages, which is what you want for a carbon copy; rename a box to give that page its own value, or move and delete boxes freely since each page keeps its own copy. Press + blank for a page with no boxes. Switch pages with the numbered buttons.
- Type a template name and Save.
- Fill values and Render for one page, or use the bulk flow below.
Your work is not lost if you forget to save. Changes are written to the working file about a second after you make them, and opening a different template from the Open list keeps the current layout first (under the name in the Template box, or as "draft"), so you can reopen it from that same list.
- Click Download XLSX to get a sheet with one column per field, across all
pages. A date field becomes three columns (
name.day,name.month,name.year); a choice column holds the selected option key. Every column is formatted as text, so values keep their leading zeros. - Fill one row per form.
- Pick the file and click Generate from XLSX.
- Images land in
workspace/output/<template>/, each named by a field whose name contains "serial" if present, otherwise by row number. With a multi page template each row writes its pages in sequence:0338610.png,0338610_p2.png,0338610_p3.png, and so on.
Templates are JSON: global settings plus a list of pages, each with its own base
image and fields. See examples/fields.example.json.
{
"font": "fonts/YourFont.ttf", "ink": [20,20,20], "default_size": 22,
"pages": [
{ "name": "page1", "base_image": "bases/front.jpg", "fields": [ ... ] },
{ "name": "page2", "base_image": "bases/back.jpg", "fields": [ ... ] }
]
}
A field looks like:
{
"name": "full_name",
"type": "text",
"box": [x0, y0, x1, y1],
"value": "",
"font": "fonts/YourFont.ttf",
"color": "#112233",
"quad": [[x,y],[x,y],[x,y],[x,y]]
}
type is one of text, date, choice, digits. font, color and quad
are optional; font and color fall back to the global defaults. quad holds
the four corners (top left, top right, bottom right, bottom left) that the text
is warped into for 3D depth; without it the text is drawn flat in box.
Single page files that use a top level base_image and fields still load: they
are read as a one page template.
formforge/ the package (app, server, renderer, batch) and web/ UI
scripts/ build_exe.py (package as .exe), make_demo.py (build the gif)
examples/ a sample template
assets/ images used by this readme
workspace/ your data: form images, fonts, templates, output (git ignored)
pip install pyinstaller
python scripts/build_exe.py
This produces dist/FormForge.exe, a single double click file. A workspace
folder for your data is created next to the .exe on first run, so keep the .exe
in its own folder.
This build of Pillow has no HarfBuzz, so text is shaped with arabic-reshaper
and python-bidi. A font aware fallback swaps any presentation form glyph a
font does not include for an equivalent it does, so different fonts render
without missing letters.
MIT. See LICENSE.
