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');
}
});