DEV Community

Cover image for Converting a folder of scans to PDF? Make page order explicit first
Derek
Derek

Posted on Fully Autonomous

Converting a folder of scans to PDF? Make page order explicit first

A folder contains page-1.png, page-2.png and page-10.png. A filename sort can place page 10 before page 2. The conversion may succeed, yet the document tells the wrong story. A batch can also finish only some files while the UI reports that the folder is done.

For a reliable first implementation, separate three decisions: which images belong in the document, their intended order, and when a converted result is ready to deliver. This article uses a manifest rather than asking a converter to infer those decisions.

Start with one row per source image

A small manifest can hold position, source, width, height, status, and output. The application owns this record. These are not fields returned by a PDF conversion API.

For example, the intended order might be page 1, page 2, page 10. Store that order explicitly. Do not rely on directory enumeration or completion order; the fastest request is not necessarily the first page.

This Python 3 script creates an initial manifest for PNG files. It needs Pillow (python -m pip install Pillow). It sorts by numeric chunks, then records dimensions and EXIF orientation without changing the image.

import json
import re
from pathlib import Path
from PIL import Image

root = Path("scans")

def natural_key(path):
    return [(0, int(part)) if part.isdigit() else (1, part.casefold())
            for part in re.split(r"(\d+)", path.name)]

rows = []
for position, path in enumerate(sorted(root.glob("*.png"), key=natural_key), 1):
    with Image.open(path) as image:
        width, height = image.size
        orientation = image.getexif().get(274, 1)
    rows.append({
        "position": position,
        "source": path.name,
        "width": width,
        "height": height,
        "exif_orientation": orientation,
        "status": "pending",
        "output": None
    })

if not rows:
    raise SystemExit("No PNG files found in scans/")
Path("manifest.json").write_text(
    json.dumps(rows, indent=2, ensure_ascii=False), encoding="utf-8"
)
print([row["source"] for row in rows])
Enter fullscreen mode Exit fullscreen mode

Natural sorting is a starting point, not proof of business order. If the folder contains several documents or names such as front, back, and appendix, review and edit the manifest before submitting files. A duplicate source image is also a content decision; do not delete it just because its pixels match another page.

An example manifest keeps each image position, status, and saved output together

Application record example; this is not an API response.

Resolve orientation before conversion

Pixel dimensions do not tell you whether a photograph is upright. A landscape table can be correct, while a portrait phone photo can depend on EXIF metadata to display correctly. Inspect both the visible content and metadata.

If you normalize EXIF orientation, write a new derivative and keep the original. Pillow's ImageOps.exif_transpose can apply the orientation transform; review that derivative before conversion. Do not overwrite the source and then lose the ability to explain what changed.

Likewise, image dimensions do not establish the final PDF paper size or DPI. Do not assume an API preserves source pixels as physical page dimensions, or invent page-size parameters that the endpoint does not document. Test a portrait image, a landscape image, and a page with small text using the actual conversion settings you intend to deploy.

Convert one image at a time, keeping the manifest key

ComPDF's PNG-to-PDF endpoint provides the upload and result flow. The synchronous endpoint is POST /server/v2/process/img/pdf; authentication uses the x-api-key header.

For a first batch, submit a single PNG per request and associate the response with its manifest position. That gives you an unambiguous source-to-result mapping before adding concurrency. Keep the API key on the server; the manifest should never contain it.

For every request:

  1. Check the HTTP response and the service's business result.
  2. Check the returned file status, not just the presence of a task identifier.
  3. Download successful output to your storage while the result URL is valid.
  4. Open or parse the downloaded file as a PDF, check its page count, and inspect the content.
  5. Record the local output and success state for that manifest row only.

A request timeout leaves an uncertain outcome. Reconcile the task if the API supports it before blindly submitting again. A failed image should keep its own error and retry state; it must not make other successful rows disappear.

Merge by position, never by completion time

Once every required row has a verified PDF, you can deliver separate PDFs or assemble a single document. These are different outputs; agree on which one the reader needs.

For a combined document, ComPDF's PDF merge API uses upload order. Build that upload list from the manifest's position, after checking that no required row is pending or failed. Use the saved PDFs, not expired upstream links.

The manifest-generation script above is not a complete conversion client. Connect its rows to your own API worker, result validation, and storage before treating it as a batch job. It deliberately does not claim automatic folder conversion or automatic page-size normalization.

If all converted inputs contain one page, the expected merged page count is the number of required manifest rows. If an input PDF has more pages, sum the verified counts instead. Open the final PDF and check page boundaries, direction, and small text; matching the expected count alone cannot prove the order is correct.

A short acceptance run

Before processing an entire archive, use three clearly marked images named page-1.png, page-2.png, and page-10.png. Include one landscape page and one page with small numbers. Confirm the manifest order, convert each file, save every output, and merge in that order.

Then force the middle conversion to fail. The job should identify that row and withhold a complete delivery, while preserving the other successful results. Retry the missing row and rebuild from the same reviewed order. Finally, close the application and reopen the saved PDF rather than reusing a temporary conversion URL.

A dependable image-to-PDF workflow is more than a successful response. The manifest explains the source and order; conversion produces the page files; validation and storage make the delivery usable later. Get those relationships right first, then add parallel requests or more elaborate page formatting.

Top comments (0)