npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@stll/anonymize-pdf

v2.9.0

Published

Fail-closed PDF coverage and risk inspection contracts for stella anonymization

Readme

@stll/anonymize-pdf

Bounded PDF inspection and destructive raster anonymization contracts.

Inspection results inventory structures that can retain sensitive data, including forms, annotations, attachments, metadata, JavaScript, XFA, optional content, signatures, and image objects. A black rectangle drawn over a PDF page is not redaction because the original text or image can remain underneath it.

Incremental revisions and non-whitespace bytes after the final %%EOF marker are reported as retained document bytes and prevent full coverage. The current object graph is inspected; superseded revision contents are not claimed as parsed or safe.

import { readFile } from "node:fs/promises";
import { inspectPdf } from "@stll/anonymize-pdf";

const inspection = inspectPdf(await readFile("contract.pdf"));
console.log(inspection.risks);
console.log(inspection.coverage);

Without page observations from a renderer/OCR adapter, coverage is explicitly partial with page-content-not-observed. The optional observation contract uses UTF-16 text offsets and PDF points. Page dimensions must match the effective CropBox/MediaBox intersection after page rotation and UserUnit scaling (within 0.25 points for renderer rounding). A complete text layer must map every observed UTF-16 code unit to glyph geometry. It is the seam for a later PDFium-backed renderer and destructive raster workflow; callers must not describe inspection alone as anonymization.

Glyph bounds use a normalized displayed-page coordinate space in PDF points: the effective visible page's bottom-left is (0, 0), and CropBox translation, page rotation, and UserUnit scaling have already been applied.

Coverage is only full when every page was rendered, its text layer was completely observed, and OCR completely covered the rendered page pixels. That includes vector outlines, not only PDF image objects.

lopdf enforces the decompression ceiling per object or cross-reference stream during parsing. The inspector additionally bounds aggregate loaded string, name, dictionary-key, and stream payload after parsing. That aggregate check is not a guarantee that the parser allocated no intermediate memory before the check; callers must also enforce the input-byte limit and process isolation appropriate for untrusted files.

Destructive raster anonymization

anonymizePdfRaster accepts pages produced by an explicit renderer/OCR provider and creates a brand-new image-only PDF. It requires a prepared stella pipeline, runs native detection over each page's observed text, merges an optional digest-bound ExternalDetectionBatch, and fails if any selected non-whitespace UTF-16 span is not completely mapped to observed glyph geometry. Provider metadata must identify one explicit OCR language pack; mixed or implicit language selection is not part of the contract. It requires one opaque, row-packed RGB8 buffer for every source page, SHA-256 binds the source and each page, requires complete rendering and OCR assertions, destructively fills the mapped pixels, and verifies both the newly written PDF graph and its decompressed image pixels. Encrypted inputs are rejected.

The output never reuses source PDF objects, streams, text layers, metadata, forms, annotations, attachments, actions, signatures, optional content, or incremental revisions. A solid rectangle in this output is destructive: the original pixels are absent rather than hidden underneath it. The tradeoff is intentional loss of searchability, accessibility, links, forms, signatures, and other interactive behavior.

The package does not bundle renderer/OCR binaries or silently select a language. Its optional Node adapter invokes locally installed Poppler pdftoppm and Tesseract directly (without a shell), sequentially, in a private temporary directory with bounded output and timeouts. It renders and removes one page at a time so document-wide limits are enforced without first materializing every page raster. Callers select exactly one installed Tesseract language pack.

import {
  anonymizePdfRaster,
  renderPdfWithPopplerTesseract,
} from "@stll/anonymize-pdf";

const observed = await renderPdfWithPopplerTesseract({
  document,
  ocrLanguage: "eng",
});
const result = anonymizePdfRaster({
  document,
  pipeline,
  provider: observed.provider,
  pages: observed.pages,
});

A provider's complete OCR assertion means every rendered page was submitted to that OCR engine; it is not a claim that OCR or PII detection has perfect recall. OCR word boxes are conservatively redacted as whole regions when a selected span overlaps them. The returned certificate always says piiCleanGuaranteed: false: verification proves a fresh image-only structure and the requested destructive pixel rewrite, not perfect OCR or detector recall. Browser rendering is not part of this surface. Raster anonymization is a Node and Python document-profile capability; PDF inspection remains available through the byte-oriented core runtimes. The Poppler/Tesseract adapter is separately named as the Node-only document.pdf.observe-raster-local capability.

rewritePdfRasterFromDetections is the advanced rewrite-only seam for trusted adapters that already ran detection. Detection spans must be strictly ordered and non-overlapping. Its name and certificate deliberately do not imply detection or PII cleanliness.