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

@manojcoder/pdf-utils

v1.0.1

Published

Browser-compatible, privacy-first PDF utility library powered by pdf-lib

Readme

@manojcoder/pdf-utils

A lightweight, framework-agnostic, browser-compatible, privacy-first PDF manipulation library powered by pdf-lib. Designed specifically for client-side execution to keep all documents local and secure. It acts as an offline browser pdf editor, pdf-lib wrapper, and offline pdf modifier.


🚀 UI Version for Non-Developers

Not a developer? Prefer a visual interface? Use our free, offline web utilities directly in your browser at PDF Toolkit.


Features

  • 100% Local: No network uploads, no servers, full GDPR/SOC2 compliance by design.
  • Strict Types: Fully written in TypeScript with full JSDoc IDE Intellisense.
  • Tree-Shakable: Built using tsup targeting ESM and CommonJS formats.
  • Universal Inputs: Accepts browser File, Blob, ArrayBuffer, or Uint8Array natively.
  • Consistent Output: Always returns a standard Uint8Array.

Installation

Install using npm, yarn, or pnpm:

npm install @manojcoder/pdf-utils pdf-lib

Note: pdf-lib is a peer dependency and needs to be installed alongside this package.


Unified Input Normalization

To make the developer experience seamless in the browser, all utility functions accept a union type PDFInput:

type PDFInput = File | Blob | ArrayBuffer | Uint8Array;

Internally, these inputs are normalized into Uint8Array byte buffers using a memory-optimized reader. You do not need to manually parse file uploads or array buffers before passing them to the SDK.


Detailed API Documentation

1. unlockPDF

Loads an encrypted PDF using the provided password, saves the document without encryption restrictions, and returns the decrypted buffer.

Signature

function unlockPDF(file: PDFInput, password?: string): Promise<Uint8Array>

Example

import { unlockPDF } from "@manojcoder/pdf-utils";

const fileInput = document.getElementById("pdf-upload") as HTMLInputElement;
fileInput.addEventListener("change", async () => {
  const file = fileInput.files?.[0];
  if (!file) return;

  try {
    const password = prompt("Enter PDF Password:");
    if (!password) return;

    const decryptedBytes = await unlockPDF(file, password);
    const blob = new Blob([decryptedBytes], { type: "application/pdf" });
    const downloadUrl = URL.createObjectURL(blob);
    
    // Use downloadUrl to download or render the file...
  } catch (error) {
    console.error("Decryption failed. Incorrect password or invalid file structure.", error);
  }
});

2. mergePDFs

Merges multiple PDF documents in sequence into a single consolidated PDF document.

Signature

function mergePDFs(files: PDFInput[]): Promise<Uint8Array>

Example

import { mergePDFs } from "@manojcoder/pdf-utils";

async function combineMultiplePDFs(filesList: File[]) {
  try {
    if (filesList.length < 2) {
      alert("Please select at least 2 PDF files to merge.");
      return;
    }

    const mergedBytes = await mergePDFs(filesList);
    const blob = new Blob([mergedBytes], { type: "application/pdf" });
    const downloadUrl = URL.createObjectURL(blob);
    
    const a = document.createElement("a");
    a.href = downloadUrl;
    a.download = "merged_output.pdf";
    a.click();
  } catch (error) {
    console.error("Failed to merge PDF files:", error);
  }
}

3. splitPDF

Splits a PDF document according to range instructions (e.g., "1-3, 5, 7-10").

Signature

function splitPDF(file: PDFInput, pageRanges: string): Promise<Uint8Array[]>

Example

import { splitPDF } from "@manojcoder/pdf-utils";

async function sliceDocument(file: File) {
  try {
    const rangeExpression = "1-2, 4-4, 5-8"; // Slices: pages 1 to 2, page 4, and pages 5 to 8
    const outputFiles = await splitPDF(file, rangeExpression);

    outputFiles.forEach((bytes, index) => {
      const blob = new Blob([bytes], { type: "application/pdf" });
      const url = URL.createObjectURL(blob);
      console.log(`Document segment ${index + 1} generated successfully: ${url}`);
    });
  } catch (error) {
    console.error("Failed to split document. Check bounds and range format.", error);
  }
}

4. rotatePages

Rotates all pages (or specific pages) by a specified angle.

Signature

function rotatePages(
  file: PDFInput,
  degreesToRotate: 90 | 180 | 270,
  specificPages?: number[]
): Promise<Uint8Array>

Example

import { rotatePages } from "@manojcoder/pdf-utils";

async function rotateSelectedPages(file: File) {
  try {
    // Rotates the first page (index 0) and third page (index 2) by 90 degrees clockwise
    const rotatedBytes = await rotatePages(file, 90, [0, 2]);
    const blob = new Blob([rotatedBytes], { type: "application/pdf" });
    // Use the rotated blob...
  } catch (error) {
    console.error("Rotation operation failed:", error);
  }
}

5. extractPages

Extracts specific 0-indexed pages and compiles them into a new PDF document.

Signature

function extractPages(file: PDFInput, pagesToExtract: number[]): Promise<Uint8Array>

Example

import { extractPages } from "@manojcoder/pdf-utils";

async function extractKeyPages(file: File) {
  try {
    const pages = [0, 1, 4]; // Extracts page 1, 2, and 5
    const extractedBytes = await extractPages(file, pages);
    const blob = new Blob([extractedBytes], { type: "application/pdf" });
    // Use extracted document blob...
  } catch (error) {
    console.error("Page extraction failed:", error);
  }
}

6. removePages

Removes specific 0-indexed pages from a PDF document.

Signature

function removePages(file: PDFInput, pagesToRemove: number[]): Promise<Uint8Array>

Example

import { removePages } from "@manojcoder/pdf-utils";

async function deleteUnwantedPages(file: File) {
  try {
    const pagesToDelete = [2, 3]; // Deletes third page (index 2) and fourth page (index 3)
    const sanitizedBytes = await removePages(file, pagesToDelete);
    const blob = new Blob([sanitizedBytes], { type: "application/pdf" });
    // Use sanitized document blob...
  } catch (error) {
    console.error("Page removal failed:", error);
  }
}

7. reorderPages

Reorders pages in the document based on a new sequence index array.

Signature

function reorderPages(file: PDFInput, newOrder: number[]): Promise<Uint8Array>

Example

import { reorderPages } from "@manojcoder/pdf-utils";

async function rearrangeDocumentSequence(file: File, totalPagesCount: number) {
  try {
    // Reverse the sequence order of the entire PDF
    const reversedOrder = Array.from({ length: totalPagesCount }, (_, i) => i).reverse();
    
    const reorderedBytes = await reorderPages(file, reversedOrder);
    const blob = new Blob([reorderedBytes], { type: "application/pdf" });
    // Use reordered blob...
  } catch (error) {
    console.error("Reorder operation failed:", error);
  }
}

8. imagesToPDF

Compiles an array of images (JPEG or PNG) into a single PDF document. Automatically matches page dimensions to dimensions of the images.

Signature

function imagesToPDF(images: PDFInput[]): Promise<Uint8Array>

Example

import { imagesToPDF } from "@manojcoder/pdf-utils";

async function createPDFFromImages(fileList: File[]) {
  try {
    // Convert array of image files into a single PDF
    const pdfBytes = await imagesToPDF(fileList);
    const blob = new Blob([pdfBytes], { type: "application/pdf" });
    const url = URL.createObjectURL(blob);
    window.open(url);
  } catch (error) {
    console.error("Image compilation failed:", error);
  }
}

9. editMetadata

Edits metadata properties of a PDF document (Title, Author, Subject, Creator, Producer, Keywords).

Signature

interface MetadataOptions {
  title?: string;
  author?: string;
  subject?: string;
  creator?: string;
  keywords?: string[];
  producer?: string;
}

function editMetadata(file: PDFInput, metadataOptions: MetadataOptions): Promise<Uint8Array>

Example

import { editMetadata } from "@manojcoder/pdf-utils";

async function addCompanyMetadata(file: File) {
  try {
    const metadata = {
      title: "Quarterly Financial Analysis",
      author: "Manoj Kumawat",
      creator: "PDF Toolkit SDK",
      keywords: ["Finance", "Report", "Q3"],
      subject: "Company Internal Auditing"
    };

    const updatedBytes = await editMetadata(file, metadata);
    const blob = new Blob([updatedBytes], { type: "application/pdf" });
    // Use updated blob...
  } catch (error) {
    console.error("Metadata modification failed:", error);
  }
}

10. addWatermark

Adds diagonal, semi-transparent watermark text to every page of a PDF document.

Signature

interface WatermarkOptions {
  fontSize?: number;
  opacity?: number;
  rotation?: number; // angle in degrees
  color?: string; // Hex color code
}

function addWatermark(
  file: PDFInput,
  text: string,
  options?: WatermarkOptions
): Promise<Uint8Array>

Example

import { addWatermark } from "@manojcoder/pdf-utils";

async function applyConfidentialWatermark(file: File) {
  try {
    const config = {
      fontSize: 56,
      opacity: 0.25,
      rotation: 45, // 45-degree angle diagonal
      color: "#EF4444" // Crimson red color
    };

    const watermarkedBytes = await addWatermark(file, "CONFIDENTIAL", config);
    const blob = new Blob([watermarkedBytes], { type: "application/pdf" });
    // Use watermarked blob...
  } catch (error) {
    console.error("Watermark overlay failed:", error);
  }
}

⚡ Performance & Memory Optimization

Because all operations execute locally on the client's machine (often within mobile browser constraints), follow these performance best practices:

  1. Explicit Memory Revocation: When converting output bytes to object URLs, always clean up the browser memory reference when the URL is no longer needed:

    const url = URL.createObjectURL(blob);
    // ... after download completes or preview is closed
    URL.revokeObjectURL(url);
  2. Garbage Collection: Avoid keeping reference pointers to large file structures or array buffers in your application state. Nuxt/React/Vue reactive objects (e.g. ref(uint8array)) can block browser garbage collection. Always store raw buffers in standard JavaScript local scopes wherever possible.

  3. Handle heavy loops asynchronously: When processing batches of large files, throttle operations or use a Web Worker thread to ensure the browser UI doesn't freeze during intensive document assembly processes.


License

MIT License