Build iLovePDF Premium

YESreplaces $9/mosaves $108/yrback to the verdict

0%0 of 20 items done

Saved on this device only. Tick prerequisites first, then work the phases in order · do not start one until the checks above it pass.

A local toolbox for the operations people actually need: merging and splitting with the page tree handled properly, compression that reduces size by fixing what is actually large, conversion to and from common formats, and every operation leaving the original untouched with a record of what produced each output.

estimated effort multi-daythe files for this build are in the project pack

Structureqpdf for page tree operationsContentmupdf or an equivalent for content inspectionConversionLibreOffice headless for office formatsRuntimePython 3.12 orchestrating themStorageOriginals untouched, outputs alongside with an operation log

Before step 1

Everything below is assumed from the first step. Tick each one when you actually have it, not when you plan to.

  1. installfree

    Why Orchestrates the tools and does the analysis.

    Get it python.org or your package manager, then a virtual environment.

    Verify python3 --version prints 3.12 or higher

  2. installfree

    Why Structural operations and byte attribution respectively. Writing either yourself means implementing parts of the PDF specification.

    Get it brew install qpdf mupdf or your package manager, plus a Python binding for inspection. open ↗

    Verify qpdf --version runs and a document's objects can be listed

  3. installfree

    Why Office document fidelity is decades of work and is the one part of this genuinely not worth attempting.

    Get it Install LibreOffice and confirm the headless conversion works from the command line. open ↗

    Verify soffice converts a test document to PDF

  4. have readyfree

    Why Every phase is tested against them: a scan, a text document with embedded fonts, an encrypted file, a damaged file, a very large one and an office document.

    Get it Collect from documents you already have. Truncate one deliberately to produce the damaged case.

    Verify All six are present and each behaves as expected

  5. installfree

    Why Validating output in the tool that produced it proves nothing, and a structurally broken file often opens fine in a forgiving viewer.

    Get it Any reader other than the one your library uses. A strict one is more useful than a forgiving one.

    Verify It opens a merged output and reports a corrupt one as corrupt

Data model

Create these before the first phase that stores anything. Changing a table later is the expensive kind of change.

- documents(id, path, pages, bytes, encrypted, has_text_layer, content_hash, imported_at)
- page_stats(document_id, page, image_bytes, text_bytes, font_bytes, other_bytes) · what is actually large, per page
- operations(id, kind, input_hashes_json, params_json, output_path, output_bytes, at) · kind is merge, split, compress, convert or rotate
- fonts(document_id, name, embedded, subset, bytes) · because unsubset embedded fonts are a common source of size
- The page statistics are what turn compression from guesswork into a decision, and they are worth computing before offering any setting.

Environment variables

These go in a .env file the app reads at startup. The pack's .env.example is this table as a file · copy it, never commit the filled-in version.

VariableNeededExampleWhere the value comes from
OUTPUT_DIRrequired./outWhere outputs and manifests are written. Never the source directory.
COMPRESS_IMAGE_DPIrequired150Target image resolution. Screen, print and archive want different values.
COMPRESS_PRESERVE_TEXTrequiredtrueNever rasterise a document that has a text layer. Effectively always true.
SOFFICE_PATHoptional/usr/bin/sofficeOnly needed if LibreOffice is not on the path. Office conversion is disabled without it.
MANIFEST_PATHrequired./out/manifest.jsonInputs, settings and outputs per operation. What makes a batch auditable.

The build, in order

  1. Structure operations that do not corrupt

    Merge, split and rotate produce valid documents that open everywhere.

    1. Page tree operations through qpdf rather than by hand. This is where files get corrupted.

      Files src/structure.py

      terminal
      qpdf --empty --pages a.pdf b.pdf -- merged.pdf
    2. Bookmarks and document properties. Losing them silently is the common annoyance.

      Files src/structure.py

    3. Open in a second reader, and check structure with a tool rather than by eye.

      terminal
      qpdf --check merged.pdf
    done when · tick each as it passes
    watch out
    • Merging by concatenating page objects without rebuilding the page tree produces a file that opens in a forgiving reader and fails in a strict one, and the failure appears at the worst moment on someone else's machine. Use a library that rebuilds the structure, and validate the output rather than trusting that it looked fine in one viewer.
  2. Finding out what is actually large

    You can say why a document is fifty megabytes before compressing it.

    1. Images, text, fonts, other. The attribution is what makes the next phase a decision.

      Files src/analyse.py

    2. A full font embedded for three characters is a common and easily fixed cause of size.

      Files src/fonts.py

    3. Three is enough. It tells you which compression approach is worth applying.

      Files src/report.py

    done when · tick each as it passes
    watch out
    • Applying a generic compression setting to every document produces poor results in both directions: a scan-heavy document stays large because the setting was gentle, and a text document is degraded for nothing because it was never image-heavy in the first place. Measure first, then choose an approach per document.
  3. Compression that keeps documents usable

    A document gets smaller without becoming unreadable or losing its text.

    1. Chosen for the use: screen, print or archive. One setting does not serve all three.

      Files src/compress.py

    2. Cheap size reductions that change nothing visible.

      Files src/compress.py

    3. Extract text before and after and compare. This is the check that prevents the worst outcome.

      Files tests/test_text_layer.py

      terminal
      pdftotext before.pdf - | wc -w
      pdftotext after.pdf - | wc -w
    done when · tick each as it passes
    watch out
    • Rasterising pages to reach a size target destroys the text layer, so the document stops being searchable, selectable and accessible, and that loss is invisible in a viewer until someone tries to copy a line. If a document has a text layer, it must still have one afterwards.
  4. Conversion in both directions

    Documents convert to and from common formats with the losses stated.

    1. Through an installed converter, entirely locally.

      Files src/convert.py

      terminal
      soffice --headless --convert-to pdf --outdir out input.docx
    2. Screen, print or archive. State which the user picked in the output name.

      Files src/rasterise.py

    3. Prominently. A user who does not know will convert back and lose everything.

      Files src/convert.py

    done when · tick each as it passes
    watch out
    • Converting a PDF back to an editable document is inherently lossy, because a PDF describes positioned glyphs rather than a document structure, and the result is frequently a mess of text boxes. Say so rather than presenting it as a round trip, since a user who edits the result and converts back will lose the layout entirely.
  5. Batch, and the awkward documents

    A folder of documents processes unattended and the difficult ones are reported.

    1. Encrypted, damaged, enormous. Report before processing rather than failing partway.

      Files src/triage.py

    2. One bad file must not cost an hour of a batch.

      Files src/batch.py

    3. Inputs, settings, outputs. What makes the batch reproducible and auditable.

      Files src/manifest.py

    done when · tick each as it passes
    watch out
    • An encrypted document in a batch will stop a naive run partway through, usually after an hour, and restarting reprocesses everything already done. Detect the awkward cases at the start of the run, report them, and process the rest.
what this build does not replace
after v1, if you want it

Need the files? The project pack on the verdict page hands your agent the whole brief · more documents & pdfs.