Build iLovePDF Premium
YESreplaces $9/mosaves $108/yrback to the verdict
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.
Before step 1
Everything below is assumed from the first step. Tick each one when you actually have it, not when you plan to.
- 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 - 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 - 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 - 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 - 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.
| Variable | Needed | Example | Where the value comes from |
|---|---|---|---|
OUTPUT_DIR | required | ./out | Where outputs and manifests are written. Never the source directory. |
COMPRESS_IMAGE_DPI | required | 150 | Target image resolution. Screen, print and archive want different values. |
COMPRESS_PRESERVE_TEXT | required | true | Never rasterise a document that has a text layer. Effectively always true. |
SOFFICE_PATH | optional | /usr/bin/soffice | Only needed if LibreOffice is not on the path. Office conversion is disabled without it. |
MANIFEST_PATH | required | ./out/manifest.json | Inputs, settings and outputs per operation. What makes a batch auditable. |
The build, in order
Structure operations that do not corrupt
Merge, split and rotate produce valid documents that open everywhere.
Page tree operations through qpdf rather than by hand. This is where files get corrupted.
Files
src/structure.pyterminalqpdf --empty --pages a.pdf b.pdf -- merged.pdf
Bookmarks and document properties. Losing them silently is the common annoyance.
Files
src/structure.pyOpen in a second reader, and check structure with a tool rather than by eye.
terminalqpdf --check merged.pdf
done when · tick each as it passeswatch 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.
Finding out what is actually large
You can say why a document is fifty megabytes before compressing it.
Images, text, fonts, other. The attribution is what makes the next phase a decision.
Files
src/analyse.pyA full font embedded for three characters is a common and easily fixed cause of size.
Files
src/fonts.pyThree is enough. It tells you which compression approach is worth applying.
Files
src/report.py
done when · tick each as it passeswatch 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.
Compression that keeps documents usable
A document gets smaller without becoming unreadable or losing its text.
Chosen for the use: screen, print or archive. One setting does not serve all three.
Files
src/compress.pyCheap size reductions that change nothing visible.
Files
src/compress.pyExtract text before and after and compare. This is the check that prevents the worst outcome.
Files
tests/test_text_layer.pyterminalpdftotext before.pdf - | wc -w pdftotext after.pdf - | wc -w
done when · tick each as it passeswatch 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.
Conversion in both directions
Documents convert to and from common formats with the losses stated.
Through an installed converter, entirely locally.
Files
src/convert.pyterminalsoffice --headless --convert-to pdf --outdir out input.docx
Screen, print or archive. State which the user picked in the output name.
Files
src/rasterise.pyProminently. A user who does not know will convert back and lose everything.
Files
src/convert.py
done when · tick each as it passeswatch 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.
Batch, and the awkward documents
A folder of documents processes unattended and the difficult ones are reported.
Encrypted, damaged, enormous. Report before processing rather than failing partway.
Files
src/triage.pyOne bad file must not cost an hour of a batch.
Files
src/batch.pyInputs, settings, outputs. What makes the batch reproducible and auditable.
Files
src/manifest.py
done when · tick each as it passeswatch 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.
Verifying the offline promiseproduct builder
You can prove nothing left the machine.
The whole suite with networking disabled. Any failure is a dependency problem.
Files
scripts/test-offline.shFonts, language packs, licence checks. All three are common and all three break the promise.
Files
docs/offline-audit.mdSo a user can check the claim rather than believing it.
Files
README.md
done when · tick each as it passeswatch out- A dependency that fetches a font, a language pack or a licence check at runtime breaks the offline promise without any code of yours doing so, and it will do it silently on a machine that happens to be online. Run the tests with networking off and treat any failure as a dependency to replace rather than a test to skip.
That is the whole plan for iLovePDF Premium. What it deliberately does not cover is below · check the gaps before you call it a replacement.
- Proprietary rendering and conversion engines.
- Optical character recognition.
- Mobile applications.
- The convenience of a web tool needing no installation.
- simple hosted delivery, mobile apps, desktop tools, OCR, and batch capacity
- pixel-perfect proprietary PDF engine
- identity verification
- qualified trust services
- large template and integration ecosystem
- Redaction that removes content rather than covering it, which is a natural neighbour
- A watched folder applying a saved chain, since most of this work is repetitive
- Page-level operations such as reordering by drag, once the structural work is trustworthy
Need the files? The project pack on the verdict page hands your agent the whole brief · more documents & pdfs.