Skip to content

kaiiiiiiiii/pdf-to-image

Repository files navigation

React TypeScript Vite TanStack Router Tailwind

PDF to Image

Convert PDF pages into high‑quality images directly in your browser. Private, fast, and fully client‑side. Built with React, TypeScript, Vite, and TanStack Router, and powered by PDF.js for rendering.


Overview

PDF to Image converts each page of your PDF into PNG, JPEG, or WEBP images using in‑browser rendering. No server uploads occur; your files never leave your device.

Why this exists

I built this tool because a third‑party app at work that handles payouts for my commute tickets only accepts image uploads, not PDFs. That limitation made submitting receipts frustrating and slow. This project fills that gap by quickly converting PDF pages to images locally, so submissions are fast, private, and compatible with systems that require image formats.

Features

  • 100% client‑side: No uploads, no servers, no tracking
  • Multiple formats: PNG, JPEG, WEBP (with automatic fallback if WEBP unsupported)
  • Page selection: Pick any subset of pages per document
  • Quality controls: Scale and quality sliders for crisp outputs
  • Batch export: Download images individually or as a single ZIP
  • Password‑protected PDFs: Prompted securely in the browser
  • Accessible UI: Keyboard‑friendly controls and clear labels
  • Pre‑render and SEO: Static head configuration with meta/OG/Twitter tags

Live Demo

  • Try it out at kaiiiiiiiii.github.io/pdf-to-image

  • This app is deployable as a static site (e.g., GitHub Pages, Netlify, Vercel). See Deployment for base‑path configuration.

  • Default base path is configured for GitHub Pages (/pdf-to-image). Adjust via environment if your hosting path differs.

Quick Start

Prerequisites

  • Node.js 18+
  • pnpm

Install and run

pnpm install
pnpm dev
# open http://localhost:3000

Build and preview

pnpm build
pnpm serve

Usage Guide

  1. Add PDFs
  • Drag and drop PDFs onto the homepage dropzone, or click to browse.
  1. Configure output
  • Format: PNG, JPEG, or WEBP
  • Scale: 1.0–4.0 (multiplied by device pixel ratio under the hood)
  • Quality: 70–100% (for JPEG/WEBP only)
  1. Select pages
  • For each PDF, use per‑document controls to select all, clear, invert, or click individual page thumbnails to toggle selection.
  1. Export
  • Download individually (multiple files, one per page), or
  • Download ZIP (a single archive with files organized per source PDF)

Tip: When working with many pages or large PDFs, prefer ZIP export to avoid multiple browser save prompts.

How It Works (Architecture)

UI Controls

  • Conversion settings panel lives in src/components/ControlsPanel.tsx and exposes:
    • Image format selector (JPEG/PNG/WEBP). The WEBP slider hints reflect automatic fallback showQuality.
    • Scale slider: Controls output resolution; final pixel size ≈ page size × scale × device pixel ratio (DPR).
    • Quality slider: Enabled for JPEG/WEBP only.

File Naming and Output Formats

  • Filenames follow baseName-pNN.ext via fileNameFor(), e.g., my‑file-p01.jpg
  • Formats:
    • PNG: Lossless, supports transparency
    • JPEG: Lossy, smallest sizes for photographic content
    • WEBP: Modern codec, great compression/quality; app auto‑falls back to PNG if unsupported effectiveFormat()
  • Background handling: A white background is prefilled to avoid JPEG artifacts on transparent PDFs renderPageToCanvas()

Privacy and Security

  • 100% local processing within your browser
  • No network uploads are performed for PDF rendering or image generation
  • Password‑protected PDFs prompt securely via window.prompt within addFiles() and are passed into openPdf()

Browser Support

  • Modern evergreen browsers: Chromium, Firefox, and Safari on desktop and mobile
  • WEBP support is auto‑detected; fallback behavior ensures compatibility
  • Note: Extremely large pages or very high scale on low‑memory devices may cause memory pressure

Project Structure

src/
├─ components/                # UI components
│  ├─ ui/                     # Shadcn/Radix-based primitives
│  ├─ ControlsPanel.tsx
│  ├─ Dropzone.tsx
│  ├─ FileList.tsx
│  └─ PageThumbnail.tsx
├─ lib/                       # Core utilities
│  ├─ pdf/
│  │  ├─ export.ts            # Canvas encoding, filenames, format logic
│  │  ├─ render.ts            # PDF.js open/render helpers
│  │  └─ worker.ts            # PDF.js worker bootstrap
│  ├─ config.ts               # Auto-generated from package.json
│  ├─ download.ts             # Client-side download helpers
│  ├─ feature.ts              # Feature detection (WEBP, DPR, etc.)
│  ├─ utils.ts
│  └─ zip.ts                  # ZIP creation with JSZip
├─ routes/
│  ├─ __root.tsx              # Document shell & head (SEO/OG/Twitter)
│  └─ index.tsx               # Main app route and logic
├─ env.ts                     # Type-safe environment configuration
└─ styles.css                 # Tailwind styles

Deployment

Static hosting

  • The app is a fully static SPA. Compatible with GitHub Pages, Netlify, Vercel, AWS S3 + CloudFront, etc.
  • Ensure the base path is set correctly when hosting under a subpath.

GitHub Pages

  1. Base path
  • Default is /pdf-to-image via src/env.ts
  • Override at build time if needed, e.g.:
VITE_BASE_PATH=/my-subpath pnpm build
  1. Build
pnpm build
  1. Deploy
  • Publish the contents of dist/ to your Pages target (gh-pages branch or GitHub Pages config)

Netlify/Vercel

  • Usually root “/”; adjust VITE_BASE_PATH if deploying under a non‑root subpath

Prerendering

  • TanStack Start prerender enabled with link crawling disabled vite.config.ts. The main route is static and works with static hosting.

Testing

Unit Tests (Vitest)

pnpm test
  • Tests live under src/lib/tests/ and use @testing-library/react/jsdom as needed

End‑to‑End Tests (Playwright)

pnpm playwright:install
pnpm test:e2e
  • Specs live under tests/e2e
  • Base URL defaults to http://localhost:3000/pdf-to-image playwright.config.ts
  • Playwright can auto‑launch the dev server when needed playwright.config.ts
  • For custom base paths, set PLAYWRIGHT_BASE_URL or VITE_BASE_PATH

Scripts

Common scripts from package.json:

  • dev: Start Vite dev server on port 3000
  • build: Runs update-config then Vite build
  • serve: Preview production build locally
  • test / test:e2e / playwright:install
  • lint / format / check / check-unused
  • update-config: Generates a typed config file from package.json

Generated config

  • The script updates src/lib/config.ts from package.json using [scripts/update-config.js](scripts /update-config.js:21). It stores:
    • version (from package.json "version")
    • homepage (from package.json "homepage", or default)

Accessibility

Performance Tips

  • Increase Scale for sharper images on HiDPI/Retina displays. Note that memory use grows with page size and scale.
  • Prefer WEBP where supported for a good quality/size balance; fallback is automatic effectiveFormat().
  • For many pages, use ZIP export to avoid multiple simultaneous download prompts.

Troubleshooting

  • “Failed to open …”
    • The PDF may require a password or be corrupted. You will be prompted as needed addFiles(), openPdf().
  • “No pages selected.”
  • Blank/low‑quality output
    • Increase Scale; ensure the original PDF page isn’t a low‑res bitmap.
  • Multiple downloads blocked by browser
    • Use “Download ZIP” for a single save prompt zipFiles().

FAQ

  • Does my PDF leave my device?
    • No. All processing happens in your browser; nothing is uploaded.
  • What’s the best format?
    • For general use, WEBP (if supported) offers excellent quality with small size; otherwise PNG for graphics or JPEG for photos.
  • Why are JPEGs on transparent PDFs white?
    • JPEG has no alpha channel; the app fills a white background to avoid visual artifacts renderPageToCanvas().
  • Can I convert password‑protected PDFs?
    • Yes. You will be prompted for the password when required openPdf().
  • Can I host the app in a subfolder?
    • Yes. Set VITE_BASE_PATH before building to match your hosting path vite.config.ts.

Roadmap

  • Remember last used format/quality/scale between sessions
  • Page range presets (e.g., “odd”, “even”, “1-4,7,10‑12” input)
  • Drag‑to‑reorder output pages in ZIP
  • Dark mode preference toggle
  • Optional transparent background for PNG/WEBP when safe for UX

Contributing

  1. Fork and clone the repository
  2. Create a feature branch: git checkout -b feat/your-feature
  3. Make changes with tests
  4. Run checks: pnpm check && pnpm test && pnpm test:e2e
  5. Commit and push
  6. Open a pull request

Code Style

  • TypeScript‑first
  • ESLint and Prettier enforced
  • Keep UI accessible and responsive
  • Prefer small, pure utilities in src/lib

License

MIT.

Acknowledgements

  • PDF.js for robust PDF rendering
  • TanStack Router/Start for routing and prerender support
  • Tailwind CSS and Radix primitives (via shadcn/ui)
  • Lucide Icons for SVG icons

Appendix: Key APIs and Entry Points

About

Convert PDF pages to images in your browser—drag, preview, and export as PNG/JPEG/WebP with client-side privacy.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors