JSPM

  • Created
  • Published
  • Downloads 88
  • Score
    100M100P100Q66140F
  • 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. 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)
  • 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.

Loading Strategy:

  1. Check if already loaded - Reuses existing window globals if present
  2. Load bundled files - Loads pdf-lib.min.js, pdf.min.js, and pdf.worker.min.js from the package
  3. Ready to compress! - No external dependencies or network requests

Simple, reliable, and works everywhere (browser, offline, corporate firewalls, etc.)

Forcing / Overriding CDNs

Before calling compressPdfClient you may override URLs:

window.PDFPRESSOR_CLIENT_CDNS = {
  pdfLib: [
    'https://my.cdn.example/pdf-lib.min.js'
  ],
  pdfjs: [
    'https://my.cdn.example/pdf.min.js'
  ],
  pdfjsWorker: [
    'https://my.cdn.example/pdf.worker.min.js'
  ]
};

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

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
) => 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
  3. Export canvas as JPEG (toDataURL('image/jpeg', quality)) and convert to bytes
  4. Embed compressed images into a fresh PDF (pdf-lib)
  5. Produce output artifacts and stats
  6. If logs is true, output timing for load, render, encode, embed, and final save

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 web workers for large batch compression to keep UI responsive
  • 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