Client-side
WATERMARK PDF
Stamp a customizable text watermark over every page of your PDF file completely in your browser.
Drag & drop a PDF file here, or click to browse
Supports single .pdf file
About Watermark PDF Tool
Protect your intellectual property or label document status by stamping custom text watermarks (e.g. 'CONFIDENTIAL', 'DRAFT', or your brand name) on every page of your PDF file. Customize the text, color, rotation angle, size, and opacity to fit your branding.
The watermarking process is performed client-side using pdf-lib. No external server receives your document, keeping your pre-release drafts and confidential files secure.
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 watermarkTextInput = document.getElementById('watermarkText');
const fontSizeInput = document.getElementById('fontSize');
const textColorSelect = document.getElementById('textColor');
const textOpacityInput = document.getElementById('textOpacity');
const textRotationSelect = document.getElementById('textRotation');
const watermarkBtn = document.getElementById('watermarkBtn');
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' : ''}`;
configPanel.classList.remove('hidden');
resultContainer.classList.add('hidden');
} catch (err) {
console.error(err);
alert('Could not read PDF file. Make sure it is not encrypted.');
}
}
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];
}
watermarkBtn.addEventListener('click', async () => {
if (!currentPdfBytes) return;
const text = watermarkTextInput.value.trim();
if (!text) {
alert('Please enter watermark text.');
return;
}
configPanel.classList.add('hidden');
loadingContainer.classList.remove('hidden');
resultContainer.classList.add('hidden');
try {
const { PDFDocument, rgb, StandardFonts, degrees } = PDFLib;
const pdfDoc = await PDFDocument.load(currentPdfBytes);
const font = await pdfDoc.embedFont(StandardFonts.HelveticaBold);
const fontSize = parseInt(fontSizeInput.value) || 50;
const opacity = parseFloat(textOpacityInput.value) || 0.3;
const rotation = parseInt(textRotationSelect.value) || 0;
let color = rgb(0.5, 0.5, 0.5); // Gray default
const colorVal = textColorSelect.value;
if (colorVal === 'red') color = rgb(0.9, 0.1, 0.1);
else if (colorVal === 'blue') color = rgb(0.1, 0.3, 0.9);
else if (colorVal === 'black') color = rgb(0.0, 0.0, 0.0);
const pages = pdfDoc.getPages();
for (const page of pages) {
const { width, height } = page.getSize();
const textWidth = font.widthOfTextAtSize(text, fontSize);
// Render centered watermark
page.drawText(text, {
x: width / 2 - (textWidth / 2) * Math.cos(rotation * Math.PI / 180),
y: height / 2 - (fontSize / 2) * Math.sin(rotation * Math.PI / 180),
size: fontSize,
font: font,
color: color,
rotate: degrees(rotation),
opacity: opacity
});
}
const watermarkedBytes = await pdfDoc.save();
const blob = new Blob([watermarkedBytes], { type: 'application/pdf' });
const blobUrl = URL.createObjectURL(blob);
downloadBtn.href = blobUrl;
const nameWithoutExt = fileNameStr.replace(/\.[^/.]+$/, "");
downloadBtn.download = `${nameWithoutExt}_watermarked.pdf`;
resultInfo.textContent = `Applied watermark to ${totalPages} page(s). File size: ${formatBytes(blob.size)}.`;
loadingContainer.classList.add('hidden');
resultContainer.classList.remove('hidden');
} catch (err) {
console.error(err);
alert('An error occurred while watermarking PDF.');
loadingContainer.classList.add('hidden');
configPanel.classList.remove('hidden');
}
});