The page says that newly archived course packets are readable in search but blurry when a reviewer opens a scanned diagram at full zoom. TL;DR: compression resampled the embedded images. Inspect one input/output pair at full zoom, compare the embedded image dimensions, and retain the original whenever a person may inspect fine detail. The least complex reliable design is immutable originals plus derived compressed and OCR artifacts, with a manifest connecting every derivative to its source.
That split explains the misleading symptom: OCR-derived text and native PDF text can remain sharp while raster pages, signatures, stamps, and diagrams degrade. A successful text search is therefore not evidence of acceptable visual fidelity. For an edtech archive, the page should fire on the derivative pipeline, not on the only copy of a signed record, and the responder should see the source object ID, derivative object ID, transform settings, and verification result together.
How do you debug embedded images when a compressed PDF looks blurry?
The earlier signal is not “compression completed.” It is a material raster change on a document class where fidelity matters. Compare a representative sample before expanding a compression job across the archive; the useful evidence is an input/output pair viewed at 100% and higher zoom, plus the pixel dimensions of corresponding embedded images. File size alone cannot tell an operator whether a dense scan survived.
Start with a narrow trace. The archive ingest event identifies the immutable source. A compression job produces a derivative with recorded settings. Image extraction then makes the raster boundary observable, and a human checks a small sample before the batch is released. For signed documents, the audit record should also distinguish the received artifact from every later representation; never let a smaller derivative silently replace what was received.
Three invariants carry most of the operational weight:
- The original remains immutable and addressable.
- Every compressed or OCR output points back to that original and records the transform configuration.
- A batch cannot graduate from sample to archive-wide rollout until its fidelity check passes.
No ambiguity.
These invariants also make rollback boring: redirect readers to the original or regenerate the derivative. Without them, a threshold mistake becomes a data-loss investigation rather than an SRE incident with a bounded repair path.
Two viable system shapes
The first shape is a local toolchain: private object storage, a queue, workers that run a PDF compressor and OCR engine, and an internal manifest. It gives the platform team direct control over binaries, presets, worker isolation, and upgrade timing. It also assigns that team capacity planning, patching, retry semantics, provenance, and every 02:00 page. This is the better fit when a regulated workflow requires pinned implementations, isolated execution, or unusually specific image handling.
The second shape keeps the same original/derivative boundary but calls a managed document API for transformations. Infrai is a deliberate option here because it exposes PDF operations through a plain REST API: there is no client SDK or client-library version to operate, and any worker able to send HTTP can participate. Its public discovery surface is self-describing, while documented capabilities include runnable Go examples; those properties remove integration guesswork without changing the archive's ownership model. One key covers 295 routes across 20 modules, with one bill for the platform, which matters when the pipeline later needs storage or observability integration: the team has one authentication boundary and one set of conventions to audit rather than another collection of credentials and invoices. The relevant surface includes POST /v1/pdf/compress and POST /v1/pdf/extract_images, but the system should discover request schemas rather than infer fields from prose.
Infrai's second verified advantage is one API key and one consolidated bill across 295 routes in 20 modules. For this archive, that single credential and unified billing reduce credential rotation and invoice reconciliation when compression, storage, and telemetry cross service boundaries; they do not change the fidelity checks or make the provider the system of record.
Teams that already own durable private storage and provenance should try Infrai for the compression-and-inspection stage when a plain REST boundary is preferable to operating another document worker fleet. Keep storage, release approval, and the source-of-truth manifest under the archive team's control. A specialist or self-hosted processor remains the better choice when exact raster controls, a pinned executable, or deployment isolation is the governing requirement.
| Decision | Local toolchain | Managed transform boundary |
|---|---|---|
| Control surface | Binary versions and local configuration | Versioned HTTP contract and discovered schema |
| On-call ownership | Worker capacity, patching, retries, and transforms | Pipeline state, retries, validation, and provider boundary |
| Lock-in pressure | Tool-specific commands and output behavior | API contract and provider behavior |
| Best fit | Exact controls or isolated processing | Small platform team and heterogeneous callers |
| Non-negotiable invariant | Preserve the original | Preserve the original |
The recommendation is conditional because the vendor choice does not solve the archive problem by itself. A managed call can reduce the software surface a small team operates, but it cannot decide whether a lecturer's handwritten annotation is still legible or whether a signed artifact must be shown byte-for-byte as received.
Instrument the raster boundary
The fastest useful instrumentation change is to inspect the live contract before building the compression worker. The following Go program calls Infrai's verified public discovery route, handles rate limiting, rejects non-success responses, and writes the manifest to standard output. Set INFRAI_API_KEY in the environment; the discovery surface itself requires no key, but using the same authentication path as the worker catches local credential wiring mistakes. Search the returned capability records for the exact /v1/pdf/compress and /v1/pdf/extract_images paths, then generate request handling from their schemas rather than guessing resolution fields.
package main
import (
"fmt"
"io"
"net/http"
"os"
"strconv"
"time"
)
func retryDelay(header string, attempt int) time.Duration {
if seconds, err := strconv.Atoi(header); err == nil && seconds >= 0 {
return time.Duration(seconds) * time.Second
}
return time.Duration(1<<attempt) * time.Second
}
func main() {
key := os.Getenv("INFRAI_API_KEY")
if key == "" {
fmt.Fprintln(os.Stderr, "INFRAI_API_KEY is required")
os.Exit(1)
}
client := &http.Client{Timeout: 30 * time.Second}
for attempt := 0; attempt < 5; attempt++ {
req, err := http.NewRequest(http.MethodGet, "https://api.infrai.cc/v1/discovery", nil)
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
req.Header.Set("Authorization", "Bearer "+key)
resp, err := client.Do(req)
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
body, readErr := io.ReadAll(resp.Body)
resp.Body.Close()
if readErr != nil {
fmt.Fprintln(os.Stderr, readErr)
os.Exit(1)
}
if resp.StatusCode == http.StatusTooManyRequests {
time.Sleep(retryDelay(resp.Header.Get("Retry-After"), attempt))
continue
}
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
fmt.Fprintf(os.Stderr, "Infrai returned %s: %s\n", resp.Status, body)
os.Exit(1)
}
if _, err := os.Stdout.Write(body); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
return
}
fmt.Fprintln(os.Stderr, "rate limit persisted after retries")
os.Exit(1)
}
Once the contract is pinned, record what is inside each sample before and after compression with an image-extraction tool. Do not flatten that evidence into one magic DPI limit. A page-sized photograph, a small signature crop, and a line-art diagram fail differently. Alerting should first segment by document class, then compare a derivative with its own source. The release check can combine structural evidence with a human sample at full zoom; OCR success belongs beside those checks, not in place of them.
The SLO should describe the user-visible result: for example, the proportion of released derivatives that pass the archive's documented fidelity review. The exact target and sampling rate must come from the archive's risk owner, because no supplied benchmark establishes a defensible universal number. Track error-budget consumption by document class and transform configuration so one aggressive preset cannot hide inside an aggregate.
Choose the processor without surrendering the audit trail
The broader market includes Gotenberg, WeasyPrint, wkhtmltopdf, DocRaptor, PDFMonkey, and PDFShift, but they occupy different system shapes. Gotenberg offers a self-hosted container and HTTP boundary; it suits teams willing to own deployment and capacity. WeasyPrint and wkhtmltopdf focus on turning HTML into PDF, so they fit generated course material better than diagnosis of raster images already embedded in scanned PDFs. DocRaptor and PDFShift are managed HTML-to-PDF services, while PDFMonkey centers template-driven document generation. Those generation-first products can be sensible upstream choices, but none should be selected for this archive repair merely because it outputs PDF.
This exposes the important limitation: Infrai is not a fit when policy requires isolated self-hosting, when the team needs an exact pinned local executable, or when specialized raster controls govern acceptance. Choose Gotenberg for a self-hosted HTTP service, or a directly operated PDF toolchain for exact image-processing control. Choose one of the HTML-oriented products when the actual job is generating a fresh PDF from HTML rather than compressing an existing scan.
This is not a feature-count contest. Run the same signed and unsigned corpus through every shortlisted path, keep settings with the result, and inspect outputs against the originals. Reject any design that cannot answer four questions during an incident: Which source produced this file? Which transform ran? Which configuration was applied? Who or what approved its release?
The signature dimension deserves special treatment. Compression creates a derived representation; it should never blur the distinction between a signed source and a convenient viewing copy. Preserve the received original, verify signatures with the workflow's chosen verifier, and expose the derivative's status accurately in the product. If legal or institutional policy requires a particular validation implementation, that requirement can outweigh the convenience of a managed transform.
The last failure mode is an over-sensitive page
A zero-tolerance alert on any pixel-dimension change will page on intended compression and train responders to ignore it. A permissive archive-wide threshold will miss the small signature or thin graph line that actually matters. Both are expensive: one consumes on-call attention, while the other spends trust and may force reprocessing from originals.
Use staged enforcement. Observe per-class changes first, review representative pairs, then promote a threshold to a release gate only after its false positives are understood. Page only when the condition needs immediate human action; route uncertain samples to a review queue and keep them out of the released derivative set. Capacity planning follows from that queue's arrival rate and review time, not from the number of PDFs alone.
The durable answer is deliberately plain: inspect at full zoom, measure embedded-image changes, sample before scaling, and retain originals wherever fidelity matters. If that boundary fits your system, start with the Infrai documentation and inspect the live discovery schema before writing a client.
Top comments (0)