The context

The site already has a handful of separate PDF tools — merge, split, compress, fill AcroForm fields, drawn signature. Each is an independent page, useful for capturing its own specific search query ("merge pdf free", "compress pdf online"). What was missing was a real editor: a single workspace where you could work on a PDF with multiple tools in the same session, without bouncing between tools and re-downloading the file at every step.

The architectural call was to leave the existing pages untouched — they stay, they keep doing their SEO job — and build editor-pdf.html as a new, separate tool that functionally absorbs them into one richer interface: a thumbnail sidebar, drag&drop page reordering and rotation, annotations (free text, highlight, shapes), redaction, drawn signature, watermark. Same constraint as always: everything client-side, no upload, the file never leaves the user's browser.

Architecture: a rendering canvas plus a coordinated overlay

The base pattern isn't new on the site: it had already been validated in compila-pdf.html, which renders pages with PDF.js onto a <canvas> and layers an HTML overlay on top for interaction (fillable form fields, in that case). The editor reuses the same scheme, generalized: each page is a PDF.js rendering canvas plus an absolutely positioned overlay of the same size, where every annotation lives as an element positioned as a percentage of the page's current viewport, not in absolute pixels.

This detail matters because zoom and window resizing constantly change the rendering scale: if annotations were pixel-anchored, every resize would drift them out of position relative to the underlying text. Anchoring them as a percentage of PDF.js's current viewport keeps them consistent at any zoom level, and the conversion into absolute PDF coordinates happens only once, at final export time with pdf-lib.

Rendering is guarded by a per-page render token: whenever scale or content changes, a new token is generated, and an in-flight render, if it's no longer the most recent one, self-cancels on completion instead of drawing an already-stale frame over the new one. Without this guard, quickly resizing the window during a heavy render produces overlapping old and new frames — a classic race condition on async canvases.

The interesting part: redaction isn't a black box

Annotations, signature and watermark are, all things considered, "just" overlay management and final composition — demanding but straightforward. Redaction is different, because it has a requirement the other features don't: it must be irreversible. A tool called "redaction" that leaves the original content still present in the file, even if only visually covered, isn't doing what it claims — and in a legal or professional context, the difference between "looks covered" and "was removed" isn't a detail.

Redazione ingenua — cosa NON fare
// ❌ Disegna solo un rettangolo nero sopra il contenuto esistente
const page = pdfDoc.getPage(pageIndex);
page.drawRectangle({
  x, y, width, height,
  color: rgb(0, 0, 0)
});
// Il testo originale è ancora nel content stream della pagina:
// selezionabile, copiabile, estraibile con qualunque parser PDF

This is exactly the behavior of many "redact PDF" tools found around: they draw a shape over the text and call it redaction. Open the resulting PDF in any reader and select "all text on the page" (or, more simply, copy the black area itself), and the "hidden" content reappears in the clipboard. At the data level, the PDF hasn't lost anything — it's just gained a colored rectangle on top.

🔴

Coprire visivamente un testo in un PDF non lo elimina: il content stream della pagina resta invariato sotto la forma disegnata. Chi si affida a un blackout come questo per proteggere dati sensibili sta distribuendo comunque quei dati, solo un po' più nascosti.

The fix: rasterize, but only the pages that need it

The only way to truly remove content from a page, while staying fully client-side without a PDF content-stream rewriting engine, is to convert the whole page into an image after applying the blackout — a bitmap has no "text underneath" to extract, because there's no text left: just pixels. The obvious trade-off is that rasterizing bloats file size and loses text selectability. The approach taken applies this treatment only to pages that contain at least one redaction, leaving every other page exactly as it is in the original.

export finale — semplificato
// Per ogni pagina del documento originale...
for (let i = 0; i < totalPages; i++) {
  const hasRedaction = redactionsByPage[i]?.length > 0;

  if (!hasRedaction) {
    // ✅ Nessuna redazione: pagina copiata intatta, resta vettoriale
    const [copied] = await outputDoc.copyPages(sourceDoc, [i]);
    outputDoc.addPage(copied);
    continue;
  }

  // 🔥 Pagina con redazioni: rasterizzata ad alta risoluzione
  const bitmap = await renderPageToCanvas(pdfjsDoc, i, { scale: 3 });
  burnRedactionsIntoCanvas(bitmap, redactionsByPage[i]);
  burnAnnotationsIntoCanvas(bitmap, annotationsByPage[i]);

  const jpg = await outputDoc.embedJpg(bitmap.toJPEG(0.92));
  const newPage = outputDoc.addPage([bitmap.width, bitmap.height]);
  newPage.drawImage(jpg, { x: 0, y: 0, width: bitmap.width, height: bitmap.height });
}

Two details make the difference in this step. First: rasterization happens at a higher scale (3x) than the one used for on-screen display, otherwise text on the "burned" pages would look visibly blurrier than the vector pages left in the document. Second: on rasterized pages, even non-redaction annotations (text, highlights, shapes, signature, watermark) get burned into the same bitmap instead of being added as a separate vector overlay — otherwise you'd end up mixing different rendering types on the same page, with inconsistencies when the PDF is later reopened in different readers.

The practical result: a 20-page document with a single redaction on page 12 produces a PDF where 19 pages stay light, vector, and text-selectable, and only page 12 is an image — just as heavy as it needs to be, with nothing extractable under the blackout.

Page reordering, rotation and deletion

The thumbnail sidebar supports drag&drop reordering, 90° per-page rotation, and deletion — all operations applied to an in-memory state array (order, rotation angles, excluded pages) without touching the source document until export time. This keeps the interface responsive even on PDFs with dozens of pages, since every interaction only updates state and redraws the affected thumbnail, not the whole document.

Rotation is the one spot needing extra care on pages that end up rasterized: the viewport passed to PDF.js during rendering must already include the user-applied rotation, so the bitmap comes out with the correct orientation without needing to rotate the image afterward — rotating an already-rendered bitmap would introduce an extra, entirely avoidable resampling pass (and thus further loss of sharpness).

Drawn signature and watermark: reuse, not reinvention

The drawn signature (touch and mouse, with smoothing on the tracked points) had already been written and validated in compila-pdf.html: here it was adapted from "a form field" to "a repositionable annotation" — same stroke-capture engine, but plugged into the editor's generic overlay system, sharing the same resize handle as every other annotation. The watermark is simpler: text with configurable opacity and rotation, applicable to a single page or the whole document, also drawn as a vector overlay and only "burned in" if the page ends up rasterized because of a nearby redaction.

When rasterization-based redaction is the right choice (and when it isn't)

When to use it

  • Truly irreversible redaction is needed: covering text with a black box without rasterizing leaves the original text still extractable from the PDF — a real risk for sensitive data.
  • Client-side editing for privacy: processing the PDF entirely in the browser, with no server upload, is essential for documents with personal or confidential data.
  • Only modified pages need rasterization: rasterizing the entire document would needlessly hurt quality and file size for untouched pages.

When not to use it

  • Selectable/searchable text needs to be preserved in the final document: rasterizing converts text to image, losing the ability to copy or search it — unacceptable for some use cases (e.g. searchable archives).
  • Very long documents with many pages to redact: rasterization increases file weight; past a certain threshold, server-side tools optimized for high volumes are worth considering.
  • Only annotation is needed, not irreversible redaction: if the goal is highlighting or commenting (not permanently hiding), a simple overlay annotation layer is enough and lighter.

Alternatives

For just generating or filling PDFs with no redaction needed, tools like pdf-lib alone are enough. For summarizing rather than editing a PDF, see the approach described in summarizing a PDF with Gemini Flash-Lite, which tackles a different problem (extracting meaning, not editing the document).

Common mistakes

  • Thinking an overlaid black box is enough: without rasterization, the "covered" text still remains in the PDF's text layer and can be extracted with a simple copy-paste or a parsing tool.
  • Rasterizing the whole document out of habit: needlessly bloats pages with nothing to redact, when selective rasterization achieves the same security result with a lighter file.
  • Handling page reordering/rotation naively: without correctly syncing indices and the coordinated overlay, reordering can misalign signatures, watermarks or redactions already applied to specific pages.

What I learned

  1. "Redaction" carries an implicit irreversibility requirement that a simple graphic overlay doesn't satisfy. If the feature's name promises removal, it's worth verifying the data actually disappears, not just that it disappears from view.
  2. Rasterize selectively, not the whole document. Applying the heavy treatment only to pages that need it keeps everything else light and searchable — a clearly better trade-off than treating the entire PDF as "less trustworthy" because of one sensitive page.
  3. Anchor annotations to the viewport, not to screen pixels. Coordinates as a percentage of PDF.js's current viewport stay consistent through any zoom or resize; absolute pixel coordinates drift as soon as the rendering scale changes.
  4. A per-page render token avoids race conditions on async canvases. With repeated renders in quick succession (resize, zoom), without a way to invalidate an now-stale render you risk visually incorrect overlapping frames.
  5. Rasterization should happen at a higher scale than on-screen display. Rendering at 3x instead of screen scale keeps "burned" pages from looking noticeably blurrier than the vector pages left in the same document.
  6. Code already written for one tool gets reused for the next. The PDF.js rendering/overlay engine and drawn signature, validated in compila-pdf.html, sped up building the editor considerably instead of being rewritten from scratch.

Frequently asked questions

Why isn't a black box drawn over text in a PDF real redaction?

Because in a PDF, text is a data layer independent from the graphics: drawing a black shape over a line leaves that line in the document underneath the shape, selectable and copyable, extractable with a simple "select all" or any PDF parser, even though nothing is visible anymore. Real redaction must remove the text content from the document, not just cover it.

How do you actually remove text under a redacted area without a server processing the file?

By rasterizing the affected page: drawing it at high resolution onto a canvas via PDF.js, applying the blackout directly to the resulting bitmap, and replacing the whole vector page with that image in the final PDF. At that point the original text no longer exists as data — it's been converted to pixels along with the rest of the page.

Why is rasterizing every page of a PDF, even ones with no redactions, a bad idea?

Because an image takes up far more space than the vector text it represents, so rasterizing the whole document bloats file size and makes text unselectable and unsearchable everywhere, even where nothing needed hiding. Better to rasterize only pages with at least one redaction, copying the rest as vector.

Can you copy vector pages from a PDF and mix them with rasterized pages in the same output document?

Yes. A library like pdf-lib lets you copy original pages intact from a source PDF into a new one (keeping them vector), while also creating pages from an image and inserting them at the same position in the page index — with no visible difference in order or numbering.

Is a browser-only PDF editor actually safe for sensitive documents?

The absence of upload removes the risk of the file passing through or being stored on a third-party server: rendering, editing and export all happen in browser memory via PDF.js and pdf-lib, and the file leaves the device only when the user downloads or shares it themselves.