Studio Matrx Monthly · Volume 1 · Issue 3 · August 2026
Amogh N P
 In loving memory of Amogh N P — Architect · Designer · Visionary 
Batch-Processing Images and PDFsLesson 8.2
PSD for Architecture, Planning & Urban Design/Module 8 · Automation & the Everyday Toolkit

Lesson 8.2 · Automation & the Everyday Toolkit

Batch-Processing Images and PDFs

Pillow for whole folders of images and pypdf for document sets - resize, watermark, convert, merge, split and extract at scale

13 min Interactive lessonFree · open lessonByAmogh N P· Architect & interior designer
The hook

Two hundred 8-megabyte renders, resized, watermarked and web-ready - from one loop you wrote once.

Design produces media at volume: renders, site photos, moodboard images, scanned drawings, PDF issue sets. Preparing it - resizing for the web, stamping a studio watermark, converting formats, stitching PDFs into an issue - is slow, repetitive handwork in Photoshop or Acrobat, done the same way every time.

This is textbook automation. Two friendly libraries cover most of it: Pillow (imported as PIL) opens, transforms and saves images, and pypdf reads and rearranges PDF pages. Both install in seconds and both follow the batch shape you already know - loop over a folder, apply the same transform to each file, write the results out. This lesson turns your media chores into scripts you run and forget.

Pillow: open/transform/save. pypdf: reader.pages -> writer.add_page -> write. New files only.

Pillow - an image is a grid of pixels you can open and save

Pillow is the standard Python imaging library; you install it with pip install Pillow but import it as PIL. Its core object is Image, and the whole library revolves around three verbs: open a file into an Image, transform it, and save it back out - possibly in a different format. A single image first, to see the shape:

python
from PIL import Image

img = Image.open("render.png")
print(img.size, img.mode)   # (4000, 2250) RGBA
small = img.resize((1600, 900))
small = small.convert("RGB")
small.save("render_web.jpg", quality=82)

img.size is a (width, height) tuple in pixels; img.mode tells you the colour model (RGB, RGBA with transparency, L for greyscale). resize takes a new (width, height). The convert("RGB") matters: JPEG cannot hold transparency, so a PNG with an alpha channel must be flattened to RGB before saving as .jpg, or Pillow will error. Saving as .jpg with quality=82 is the sweet spot for web - visually clean at a fraction of the file size. Notice you changed the format simply by giving the output a different extension; Pillow infers it. That is the entire model - and to keep proportions when you only care about a maximum size, img.thumbnail((1600, 1600)) resizes in place, preserving aspect ratio and never enlarging.

Why Pillow rather than driving Photoshop from code? Because it is small, free, installs in seconds and is built precisely for this batch work - no application to open, no licence, no interface in the way. It also exposes an image's metadata: img.info and, for photographs, the EXIF data via img.getexif() can report the camera orientation, which matters because phone photos are often stored sideways with a rotation flag rather than actually rotated. Pillow's ImageOps.exif_transpose(img) applies that flag so your resized copies come out the right way up - one small call that saves a confusing folder of sideways thumbnails.

BATCH IMAGE PIPELINEphotos/200 x 8 MBresizeto 1600 pxwatermarkstudio nameweb/200 x 300 KBOne loop, same transforms on every file. Originals stay put; a new folder holds the copies.
Zoom
A batch image pipeline. One loop opens every photo in a folder, applies the same transforms - resize, then stamp a watermark - and saves a web-ready copy to an output folder, leaving the originals untouched.

The batch loop - a whole folder at once

One image is a demo; the value is in the folder. Combine last lesson's pathlib + glob with Pillow's open-transform-save, and write results to a separate output folder so originals are never touched:

python
from pathlib import Path
from PIL import Image

src = Path("photos")
out = Path("web")
out.mkdir(exist_ok=True)

for file in src.glob("*.jpg"):
    with Image.open(file) as img:
        img.thumbnail((1600, 1600))
        img.save(out / file.name, quality=82, optimize=True)
    print(f"done {file.name}")

The with Image.open(file) as img: form is worth adopting - it closes the file cleanly even if something goes wrong mid-loop, which matters when you open hundreds. thumbnail shrinks each image to fit within 1600x1600 while keeping its proportions; optimize=True squeezes a little more off the file size. The print on each iteration is a progress signal - on a big batch, silence is unnerving, and a running log tells you it is alive and where it is. One robustness habit: real folders contain surprises - a corrupt file, a stray non-image. Wrap the body in try / except so one bad file logs a warning and the loop carries on rather than crashing at photo 147 of 200. That single pattern - process each item, catch its errors, keep going - turns a fragile script into a dependable one.

For a long batch it is worth ending with a summary rather than only a wall of per-file lines. Keep a simple counter - increment a done variable on success and append to a failed list in the except - and print f"processed {done}, skipped {len(failed)}" at the end. On two hundred files that one line tells you at a glance whether the run was clean or whether a few problem files need a look, without scrolling through everything. This habit of reporting the outcome scales to every batch script you write: automation whose result you cannot see is automation you cannot quite trust.

open -> transform -> save, to a NEW folder. try/except each file so one bad photo doesn't kill the batch.

Watermarking and cropping - drawing onto images

Beyond resizing, Pillow can draw on an image through its ImageDraw module - text, lines, rectangles - which is how you stamp a studio watermark across a set of renders. Text needs a font; ImageFont.truetype loads a .ttf file at a chosen size, and you position the text with an (x, y) coordinate measured from the top-left corner:

python
from PIL import Image, ImageDraw, ImageFont

img = Image.open("render_web.jpg").convert("RGB")
draw = ImageDraw.Draw(img)
font = ImageFont.truetype("arial.ttf", 40)
w, h = img.size
draw.text((w - 360, h - 60), "STUDIO MATRX",
          fill=(255, 255, 255), font=font)
img.save("render_marked.jpg", quality=82)

Coordinates in Pillow run from (0, 0) at the top-left, x rightwards and y downwards - a small thing to remember when placing the mark. Here (w - 360, h - 60) tucks the text into the bottom-right corner regardless of image size, because it is measured back from the width and height. Cropping is equally direct: img.crop((left, top, right, bottom)) returns the rectangle between those pixel coordinates, handy for trimming borders off scanned drawings in bulk.

For a subtler mark than solid text, build the watermark on a separate transparent layer and blend it in. Create an RGBA overlay the same size as the image, draw semi-transparent text onto it (the fourth colour value is opacity, 0 to 255), and composite it with Image.alpha_composite before flattening back to RGB to save. That produces the faint, professional diagonal watermark you see on preview renders rather than an opaque stamp - and because it is all code, the placement and opacity are identical across every image in the folder. Fold either of these into the batch loop from the previous section and you have a folder-wide watermarker or auto-cropper. The lesson is that images are addressable data - a grid of pixels with a coordinate system - so anything you would do by hand to one, a loop does to a thousand.

PDF: MERGE / SPLIT / EXTRACTMERGE->many -> one setSPLIT->one -> rangesEXTRACT->lift one pageAll three just rearrange pages between files.reader.pages -> writer.add_page(...) -> writer.write(out)pypdf reads pages from one file and writes chosen pages to another.
Zoom
The three PDF moves you actually need. Merge stitches many files into one issue set; split pulls a range into its own file; extract lifts a single page out. All three are page-shuffling - no re-drawing involved.

PDFs with pypdf - merge, split, extract

PDF handling looks intimidating and is not, because the useful operations are all page-shuffling, not re-drawing. The modern library is pypdf (pip install pypdf). Its two objects mirror each other: a PdfReader opens an existing file and exposes its .pages, and a PdfWriter collects pages and writes a new file. Every operation is: read pages from one place, add the ones you want to a writer, write it out. Merging a folder of PDFs into a single issue set:

python
from pathlib import Path
from pypdf import PdfReader, PdfWriter

writer = PdfWriter()
for pdf in sorted(Path("sheets").glob("*.pdf")):
    reader = PdfReader(pdf)
    for page in reader.pages:
        writer.add_page(page)

with open("issue_set.pdf", "wb") as f:
    writer.write(f)

Splitting and extracting are the same idea in reverse. To pull pages 1 to 3 into their own file, create a fresh PdfWriter, loop for page in reader.pages[0:3] (Python slices are zero-based and the end is exclusive, so [0:3] is the first three pages), add_page each, and write. To extract a single page, add just reader.pages[4] for the fifth page. Because you always write to a new file, the source PDFs are never altered - the same safety principle as the image batches. pypdf can also rotate and re-order pages - page.rotate(90) before adding it straightens a landscape sheet scanned portrait - so a mixed, wrongly-oriented set can be normalised in the very same pass that merges it. One honest limitation: pypdf rearranges and combines pages beautifully but does not reliably edit the content inside a page or extract clean text from scanned (image-only) PDFs. For page logistics - the merge/split/extract chores that eat an afternoon before an issue - it is exactly the right tool.

Formats, quality and knowing what to hand off

A quick map of image formats saves a lot of grief. JPEG (.jpg) is lossy and small - the right default for photographs and renders headed for the web or email. PNG is lossless and supports transparency - right for diagrams, screenshots, logos and anything with crisp edges or an alpha channel. WEBP, which Pillow both reads and writes, gives noticeably smaller files than JPEG at similar quality and is now widely supported - worth reaching for when file size really matters. Because Pillow infers the format from the output extension, choosing between them is a one-line change, and a single loop can even emit several formats per source image:

python
from pathlib import Path
from PIL import Image

out = Path("web")
out.mkdir(exist_ok=True)
for file in Path("src").glob("*.png"):
    with Image.open(file) as img:
        rgb = img.convert("RGB")
        rgb.save(out / f"{file.stem}.jpg", quality=82)
        rgb.save(out / f"{file.stem}.webp", quality=80)

A word on quality that beginners often miss: repeatedly opening and re-saving a JPEG recompresses it each time and slowly degrades it, so always work from the original high-resolution file, do your resize once, and save a single web copy. If you need perfect fidelity - an archival master, a diagram - save PNG instead, which is lossless.

The same honesty from the previous lesson applies to media. A script is the right tool when the operation is uniform across many files: the same resize, the same watermark, the same conversion. It is the wrong tool for the one hero render that needs retouching by eye, or the single document that needs a bespoke layout. The craft is in the split - automate the repetitive ninety, hand-finish the meaningful ten. And keep every batch non-destructive: write to a new folder, never over your source library, because the one truly irreversible act in all of this is deleting an original you cannot regenerate.

Tools & terms in this lesson

Pillow (PIL)

Open, transform and save images

Install as Pillow, import as PIL. Image.open / resize / thumbnail / convert / save. The standard Python imaging toolkit.

Image.thumbnail

Resize keeping aspect ratio

Shrinks in place to fit a max box, never enlarges, preserves proportions. Safer than resize() when you only care about a size cap.

ImageDraw / ImageFont

Draw text and shapes on an image

How watermarking works. Coordinates run from top-left, y downwards. Needs a .ttf font file for text.

pypdf

Read and rearrange PDF pages

PdfReader.pages -> PdfWriter.add_page -> write. Merge/split/extract are lossless page-shuffling; it does not edit page content or OCR scans.

Hands-on workshop

Workshop — the web-ready media prep tool

Build a two-part script that prepares a project's media for sharing: batch-resize and watermark a folder of images, then merge a folder of PDFs into one issue set. Everything writes to new files, leaving originals safe.

Python 3, `pip install Pillow pypdf`, a `.ttf` font file for the watermark, and copies of an image folder and a PDF folder to practise on.

Given & goal
Goal: batch-resize + watermark images, then merge PDFs
Inputs: a folder of images and a folder of PDFs (copies)
Time: ~50 minutes
  1. 1Install the libraries: pip install Pillow pypdf. Confirm with from PIL import Image and from pypdf import PdfReader running without error.
  2. 2Write the image loop: create an output folder, glob the images, and for each open -> thumbnail((1600,1600)) -> save(..., quality=82). Wrap the per-file body in try / except that prints a warning and continues.
  3. 3Add watermarking: with ImageDraw and a .ttf font, stamp your studio name into the bottom-right corner (positioned from img.size) before saving.
  4. 4Write the PDF merge: make a PdfWriter, loop the sorted PDFs adding every page, and write to issue_set.pdf. Open the result and confirm the page order and count.
  5. 5Add a split: from one merged PDF, extract pages 1 to 3 into cover_set.pdf using a slice reader.pages[0:3]. Verify the extracted file opens correctly.

You’ll walk away with
A script that turns a folder of full-size images into watermarked, web-ready copies and merges a folder of PDFs into a single issue set (plus a split-out range) - robust to a bad file, with all originals preserved.

The worked example

Three altitudes on the same idea

Read the band that fits you — or all three.

For the architectAutomate busywork & build custom tools

Issue day is a media-logistics day. Renders need resizing and watermarking before they leave the office; sheet PDFs need merging into a single issue set, or a range split out for a consultant. Scripting these means the presentation set is generated identically every time, at the right size, with the right stamp, in the time it takes to fetch a coffee. Point the same tools at incoming consultant PDFs to split or extract exactly the pages you need to coordinate.

For the interior designerScripts for data, schedules & layouts

Product and moodboard imagery is your daily media load. Supplier photos arrive huge and inconsistent; a Pillow batch resizes and converts a whole folder to web-ready JPGs for a client deck in seconds, optionally watermarked. Spec and FF&E PDFs from different suppliers merge into one tidy document per room or per package with pypdf. These two scripts alone remove hours of Photoshop-and-Acrobat handwork from every project.

For the studentA hireable computational skill

Portfolios and submissions are image-and-PDF marathons. Compressing a folder of high-res renders so a portfolio PDF is emailable, watermarking work-in-progress, or stitching a submission set from many exports are all one short script each - and they teach you Pillow and pypdf on deadlines you actually face. The habit of automating your own output is exactly what a computational-design or visualization role wants to see.

Misconception check

Editing images and PDFs properly needs Photoshop and Acrobat - a script can only do crude, low-quality versions.

For batch, rule-based tasks the opposite is true: a script is often better, because it applies the exact same operation uniformly and never tires or slips on file 150. Pillow uses the same high-quality resampling and JPEG encoding as professional tools - a scripted resize is not a downgrade. pypdf's page operations are lossless, simply relocating existing pages. What a script does not replace is creative, one-off work: retouching a hero render, laying out a complex document, colour-grading by eye. The right division of labour is clear - hand-craft the few images that need judgement, and let a script handle the hundred that just need the same resize, watermark or merge. Automation targets the repetitive, not the artistic.
Try it

Do it yourself

Think through what each transform does before running it.

  1. 1Why must a transparent PNG be convert("RGB") before it can be saved as a .jpg?
  2. 2What is the difference between resize((1600, 900)) and thumbnail((1600, 1600))?
  3. 3In Pillow, where is the coordinate (0, 0) and which way does the y-axis increase?
  4. 4Describe the read-write pattern pypdf uses to merge several PDFs into one.
  5. 5Why wrap each file's processing in try / except inside a batch loop?
Take this with you

The one line to carry out

Media is addressable data: Pillow opens, transforms and saves images, and pypdf shuffles PDF pages - both in the same loop-over-a-folder shape, writing to new files. Hand-craft the images that need judgement; let a script handle the hundred that just need the same operation.
Take it further
References & further reading

Peer-reviewed journals & authoritative standards

  1. 01The Python Standard LibraryPython documentation, 2026.
  2. 02Python TutorialPython documentation, 2026.
  3. 03Python (programming language)Wikipedia, 2026.
  4. 04Library (computing)Wikipedia, 2026.
Related lessons
Recap
Design generates media in volume, and preparing it is repetitive handwork ripe for automation. Pillow opens, resizes, converts, crops and watermarks images through open-transform-save, and slots straight into a pathlib/glob folder loop. pypdf reads and rearranges PDF pages - merge, split, extract - as lossless page-shuffling. Write outputs to new folders, wrap each file in try/except, and a fragile one-off becomes a dependable batch tool.
Carry forward →

Images and PDFs are the visual deliverables. The other half of a project's paperwork is structured: schedules, BOQs, cost sheets and reports. Next we automate those - reading and writing Excel with openpyxl and generating Word documents with python-docx.

A

The author

Amogh N P

Architect, interior designer, and creative polymath. Studio Matrx began in his notebooks — his vision of design made honest, useful, and open to everyone. Its Academy is written and taught in his memory, and free, forever.

More about Amogh →