JSPM

  • Created
  • Published
  • Downloads 146
  • Score
    100M100P100Q69383F
  • License MIT

Self-contained client-side PDF compressor with bundled dependencies. No CDN required.

Package Exports

  • pdfpressor-client

Readme

pdfpressor-client

A standalone client-side PDF compressor with advanced performance optimizations. It loads a PDF in the browser, rasterizes each page to a canvas at a chosen DPI, recompresses pages as JPEG with configurable quality, and rebuilds a new PDF. Returns multiple handy artifacts (Uint8Array bytes, Blob, object URL, Buffer if available) plus detailed statistics.

Features

  • ✅ Pure browser operation (no server round-trip required)
  • ✅ Adjustable DPI (affects rendered page resolution)
  • ✅ Adjustable JPEG quality (0.1–1.0 floating value)
  • Parallel page processing - Process multiple pages simultaneously
  • Canvas pooling - Reuse canvases for better memory efficiency
  • Chunk processing - Process pages in configurable batches
  • Progress tracking - Real-time progress callbacks
  • ✅ Optional detailed logging (opt-in)
  • ✅ Side‑by‑side preview (original vs compressed first page)
  • ✅ Download links for original and compressed output
  • ✅ Simple API for React / vanilla JS

Installation (for bundlers / React)

npm install pdfpressor-client

🎯 Zero Setup Required!

This package is 100% self-contained with all dependencies bundled directly. No configuration, no file copying, no CDN fallbacks needed.

Just install and use:

import { compressPdfClient } from 'pdfpressor-client';

const { compressFile } = await compressPdfClient(pdfBytes, 'output.pdf');

What's included:

  • pdf-lib.min.js (525 KB) - bundled
  • pdf.min.js (353 KB) - bundled
  • pdf.worker.min.js (1.38 MB) - bundled

All dependencies load automatically from the package. Works offline, no external requests.

Direct Browser Usage (CDN fallback)

You can copy the contents of this folder and serve it; dependencies are loaded from CDN automatically if module imports fail.

Serve locally (example):

npx http-server . -c-1
# then open http://localhost:8080/client-compressor/index.html

How It Works

This package is completely self-contained - all PDF libraries are bundled directly with the package as native ES modules.

Loading Strategy:

  1. Check if already loaded - Reuses existing window globals if present (for compatibility)
  2. Load via ES module wrappers - Uses dedicated wrapper modules that properly export UMD bundles as ES modules
  3. Dynamic imports - Uses import() to load wrapper modules from node_modules/pdfpressor-client/
  4. Automatic worker resolution - Worker path is resolved relative to the module using import.meta.url
  5. Ready to compress! - No external dependencies, no network requests, no public folder setup

The bundled files (pdf-lib.min.js, pdf.min.js, pdf.worker.min.js) are UMD bundles wrapped by ES module files (pdf-lib-wrapper.js, pdfjs-wrapper.js) that properly export their globals. This ensures clean ES module imports that work everywhere.

Works everywhere: Browser, offline, corporate firewalls, webpack, vite, rollup, etc.

Eager Initialization (Fail Fast)

You can preload and validate all dependencies early (e.g. on app startup) to avoid first‑interaction delays:

import { initPdfCompressor } from 'pdfpressor-client';

await initPdfCompressor({ logs: true, retries: 2 });

If initialization fails it throws a PdfCompressorError with a .code value such as PDF_LIB_LOAD_FAILED, PDFJS_LOAD_FAILED, or INIT_FAILED.

Offline Usage

Because local minified copies are included, you can operate fully offline after the first install:

  • Disconnect network
  • Call initPdfCompressor() (optional) then compressPdfClient(...)
  • Loader will skip failed CDN attempts and transparently fall back to local scripts.

Environment Customization

  • Set logs=true when calling either initPdfCompressor or compressPdfClient for detailed timing and fallback logs.
  • Adjust retry attempts via initPdfCompressor({ retries: 3 }).

API

Basic Usage

compressPdfClient(
  inputBytes: Uint8Array,
  outputName: string,
  dpi?: number,     // default 150 (rendering DPI)  quality?: number, // default 0.7 (JPEG quality 0.1–1.0)
  logs?: boolean,   // default false; enable console timing + diagnostic output
  options?: {       // Advanced options (v0.2.0+)
    parallel?: boolean,           // default true - parallel processing
    chunkSize?: number,          // default 5 - pages per chunk
    reuseCanvas?: boolean,       // default true - canvas pooling
    progressCallback?: (progress: {
      current: number,           // Current page number
      total: number,            // Total pages
      progress: number          // Progress percentage (0-100)
    }) => void
  }
) => Promise<{ compressFile: {
  bytes: Uint8Array;
  blob: Blob;
  objectUrl: string|null;
  buffer: Buffer|null;
  stats: {
    outputName: string;
    originalSize: number;
    compressedSize: number;
    reduction: number;
    pageCount: number;
    dpi: number;
    quality: number;
  };
}>>

initPdfCompressor(options?: {
  logs?: boolean;     // default false
  retries?: number;   // default 2 (script tag retry count per URL)
  pdfLibUrls?: string[];     // override CDN list for pdf-lib
  pdfjsUrls?: string[];      // override CDN list for pdfjs-dist main script
  pdfjsWorkerUrls?: string[];// override CDN list for worker script
}): Promise<boolean>

Parameters

  • inputBytes (Uint8Array): Raw PDF file bytes (e.g. from file.arrayBuffer())
  • outputName (string): Suggested filename for download
  • dpi (number, default 150): Render scale; higher DPI = larger images & potentially bigger output
  • quality (number, default 0.7): JPEG quality (0.1–1.0). Lower means smaller size (more compression)
  • logs (boolean, default false): When true, enables detailed console.log, timing, and per‑page diagnostics

Returns

Single object { compressFile } where compressFile contains:

  • bytes: Compressed PDF as Uint8Array
  • blob: Compressed PDF Blob for browser download / upload
  • objectUrl: Created via URL.createObjectURL(blob) for previews
  • buffer: Node Buffer version (if Buffer exists)
  • stats: Metadata for comparison & display

Example (React)

import { useState } from 'react';
import { compressPdfClient } from 'pdfpressor-client';

function PdfCompressor() {
  const [stats, setStats] = useState(null);
  const handleFile = async (e) => {
    const file = e.target.files[0];
    if (!file) return;
    const bytes = new Uint8Array(await file.arrayBuffer());
    // Enable logs for development
    const { compressFile } = await compressPdfClient(bytes, 'compressed_' + file.name, 150, 0.7, true);
    setStats(compressFile.stats);
    const a = document.createElement('a');
    a.href = compressFile.objectUrl;
    a.download = compressFile.stats.outputName;
    a.click();
  };
  return (
    <div>
      <input type="file" accept="application/pdf" onChange={handleFile} />
      {stats && <pre>{JSON.stringify(stats, null, 2)}</pre>}
    </div>
  );
}

Example (Vanilla JS)

const inputEl = document.querySelector('#pdfInput');
inputEl.onchange = async () => {
  const file = inputEl.files[0];
  if (!file) return;
  const bytes = new Uint8Array(await file.arrayBuffer());
  // logs disabled (false) for cleaner production console
  const { compressFile } = await compressPdfClient(bytes, 'compressed_' + file.name, 144, 0.6, false);
  console.log('Reduction %', compressFile.stats.reduction.toFixed(2));
  const link = document.createElement('a');
  link.href = compressFile.objectUrl;
  link.download = compressFile.stats.outputName;
  link.click();
};

What It Accepts

  • Input: Any standard PDF file (binary bytes as Uint8Array)
  • Parameters: outputName, dpi, quality, logs

What It Outputs

  • Compressed PDF (rasterized pages) via compressFile.bytes
  • Browser-friendly Blob and objectUrl
  • Statistics comparing original vs compressed sizes

Internals / How It Works

  1. Load PDF bytes (pdfjs-dist)
  2. Render each page to a canvas at dpi/72 scale (with optional canvas pooling for efficiency)
  3. Export canvas as JPEG with specified quality
  4. Embed compressed images into a fresh PDF (pdf-lib)
  5. Process pages in parallel chunks (default) or sequentially
  6. Produce output artifacts and stats
  7. If logs is true, output timing for load, render, encode, embed, and final save

Performance Optimizations (v0.2.0+)

  • Parallel Processing: Process multiple pages simultaneously for faster compression
  • Canvas Pooling: Reuse canvas elements to reduce memory allocation
  • Chunk Processing: Process pages in configurable batches to balance speed and memory
  • Progress Tracking: Real-time callbacks for UI updates

Limitations

  • Text selectable content is lost (pages become images)
  • Very large PDFs may cause memory spikes (each page rendered to canvas)
  • JPEG only (no WebP/AVIF fallback due to pdf-lib restrictions)
  • Compression dependent on browser's JPEG encoder

Tips

  • Lower dpi (e.g. 96) and quality (e.g. 0.5) for smaller outputs
  • Keep original PDF if you need selectable text
  • Use parallel processing (default) for faster compression on modern browsers
  • Disable parallel processing on low-memory devices with parallel: false
  • Use progress callbacks to show loading indicators for large PDFs
  • Enable logs during development, disable in production for minimal console noise
  • Call initPdfCompressor() on startup to surface any blocked CDN issues early

Credits / Acknowledgements

License

MIT