Client-side

ROTATE PDF

Rotate pages of your PDF document clockwise or counterclockwise instantly. Select specific pages or rotate all.

Drag & drop a PDF file here, or click to browse
Supports single .pdf file

About Rotate PDF Tool

Rotate PDF is a handy utility for correcting the orientation of scanned documents, presentations, or sideways pages. You can choose to rotate all pages clockwise by 90, 180, or 270 degrees in a single click.

The rotation is applied directly to the document structure in your browser, preserving text searchability and image resolutions without watermarks or server-side dependencies.

Developer Reference

Core Algorithm & Standalone Script

Standalone, zero-dependency JavaScript implementation powering this tool. Free to inspect, copy, and build upon.

let currentPdfBytes = null;
    let totalPages = 0;
    let fileNameStr = '';

    const uploadZone = document.getElementById('uploadZone');
    const fileInput = document.getElementById('fileInput');
    const configPanel = document.getElementById('configPanel');
    const fileNameEl = document.getElementById('fileName');
    const pageCountEl = document.getElementById('pageCount');
    const rotationSelect = document.getElementById('rotationSelect');
    const rangeInput = document.getElementById('rangeInput');
    const rotateBtn = document.getElementById('rotateBtn');
    const loadingContainer = document.getElementById('loadingContainer');
    const resultContainer = document.getElementById('resultContainer');
    const resultInfo = document.getElementById('resultInfo');
    const downloadBtn = document.getElementById('downloadBtn');

    // Drag and drop event listeners
    ['dragenter', 'dragover'].forEach(eventName => {
      uploadZone.addEventListener(eventName, e => {
        e.preventDefault();
        uploadZone.classList.add('dragover');
      }, false);
    });

    ['dragleave', 'drop'].forEach(eventName => {
      uploadZone.addEventListener(eventName, e => {
        e.preventDefault();
        uploadZone.classList.remove('dragover');
      }, false);
    });

    uploadZone.addEventListener('drop', e => {
      const dt = e.dataTransfer;
      const files = dt.files;
      if (files.length > 0) handleFile(files[0]);
    });

    fileInput.addEventListener('change', e => {
      if (e.target.files.length > 0) handleFile(e.target.files[0]);
    });

    async function handleFile(file) {
      if (file.type !== 'application/pdf') return;
      fileNameStr = file.name;

      try {
        currentPdfBytes = await file.arrayBuffer();
        const { PDFDocument } = PDFLib;
        const pdf = await PDFDocument.load(currentPdfBytes);
        totalPages = pdf.getPageCount();

        fileNameEl.textContent = file.name;
        pageCountEl.textContent = `${totalPages} page${totalPages > 1 ? 's' : ''}`;
        rangeInput.value = ''; // empty means all pages
        
        configPanel.classList.remove('hidden');
        resultContainer.classList.add('hidden');
      } catch (err) {
        console.error(err);
        alert('Could not read PDF. Make sure it is not password protected.');
      }
    }

    function parseRanges(rangeStr, maxPage) {
      if (!rangeStr.trim()) {
        // Return all pages if empty
        const pages = [];
        for (let i = 0; i < maxPage; i++) {
          pages.push(i);
        }
        return pages;
      }

      const pages = [];
      const parts = rangeStr.split(',');

      for (let part of parts) {
        part = part.trim();
        if (!part) continue;

        if (part.includes('-')) {
          const [startStr, endStr] = part.split('-');
          const start = parseInt(startStr, 10);
          const end = parseInt(endStr, 10);

          if (isNaN(start) || isNaN(end) || start < 1 || end > maxPage || start > end) {
            throw new Error(`Invalid range: ${part}`);
          }
          for (let i = start; i <= end; i++) {
            pages.push(i - 1);
          }
        } else {
          const page = parseInt(part, 10);
          if (isNaN(page) || page < 1 || page > maxPage) {
            throw new Error(`Invalid page number: ${part}`);
          }
          pages.push(page - 1);
        }
      }

      return Array.from(new Set(pages)).sort((a, b) => a - b);
    }

    function formatBytes(bytes, decimals = 2) {
      if (bytes === 0) return '0 Bytes';
      const k = 1024;
      const dm = decimals < 0 ? 0 : decimals;
      const sizes = ['Bytes', 'KB', 'MB', 'GB'];
      const i = Math.floor(Math.log(bytes) / Math.log(k));
      return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
    }

    rotateBtn.addEventListener('click', async () => {
      if (!currentPdfBytes) return;

      const rangeStr = rangeInput.value.trim();
      const angle = parseInt(rotationSelect.value, 10);
      let pageIndices = [];

      try {
        pageIndices = parseRanges(rangeStr, totalPages);
      } catch (err) {
        alert(err.message + `. Please enter valid ranges within 1 to ${totalPages}.`);
        return;
      }

      configPanel.classList.add('hidden');
      loadingContainer.classList.remove('hidden');
      resultContainer.classList.add('hidden');

      try {
        const { PDFDocument, degrees } = PDFLib;
        const pdf = await PDFDocument.load(currentPdfBytes);
        const pages = pdf.getPages();

        pageIndices.forEach(idx => {
          const page = pages[idx];
          const currentRotation = page.getRotation().angle;
          page.setRotation(degrees((currentRotation + angle) % 360));
        });

        const rotatedPdfBytes = await pdf.save();
        const blob = new Blob([rotatedPdfBytes], { type: 'application/pdf' });
        const blobUrl = URL.createObjectURL(blob);

        downloadBtn.href = blobUrl;
        const nameWithoutExt = fileNameStr.replace(/\.[^/.]+$/, "");
        downloadBtn.download = `${nameWithoutExt}_rotated.pdf`;
        resultInfo.textContent = `Rotated ${pageIndices.length} page${pageIndices.length > 1 ? 's' : ''} by ${angle}°. File size: ${formatBytes(blob.size)}.`;

        loadingContainer.classList.add('hidden');
        resultContainer.classList.remove('hidden');
      } catch (err) {
        console.error(err);
        alert('An error occurred while rotating the PDF.');
        loadingContainer.classList.add('hidden');
        configPanel.classList.remove('hidden');
      }
    });