pdf_add_watermark
Add text watermarks to PDF documents with customizable placement, opacity, and rotation to protect content or mark documents as drafts.
Instructions
Add a text watermark to PDF pages. Watermark is centered and rotated diagonally by default. Applies to all pages if no page range is specified.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| filePath | Yes | Absolute path to the source PDF file | |
| text | Yes | Watermark text to overlay on pages | |
| outputPath | Yes | Absolute path for the watermarked output PDF | |
| pages | No | Page range, e.g. '1-5' or '1,3,5'. Omit to watermark all pages. | |
| opacity | No | Watermark opacity (0.0–1.0). Defaults to 0.3. | |
| fontSize | No | Watermark font size (10–200). Defaults to 50. | |
| color | No | Watermark color. Defaults to gray. | |
| rotation | No | Watermark rotation in degrees (0–360). Defaults to 45 (diagonal). |
Implementation Reference
- src/tools/create.ts:370-491 (handler)The tool "pdf_add_watermark" is defined and registered in src/tools/create.ts. The handler uses pdf-lib to load the PDF, draw text at the specified location, opacity, color, and rotation, and then saves the document.
server.registerTool( "pdf_add_watermark", { description: "Add a text watermark to PDF pages. Watermark is centered and rotated diagonally by default. Applies to all pages if no page range is specified.", inputSchema: z .object({ filePath: z .string() .max(4096) .describe("Absolute path to the source PDF file"), text: z.string().max(1000).describe("Watermark text to overlay on pages"), outputPath: z .string() .max(4096) .describe("Absolute path for the watermarked output PDF"), pages: z .string() .max(256) .optional() .describe( "Page range, e.g. '1-5' or '1,3,5'. Omit to watermark all pages." ), opacity: z .number() .min(0) .max(1) .optional() .describe("Watermark opacity (0.0–1.0). Defaults to 0.3."), fontSize: z .number() .min(10) .max(200) .optional() .describe("Watermark font size (10–200). Defaults to 50."), color: z .enum(["gray", "red", "blue", "green", "black"]) .optional() .describe("Watermark color. Defaults to gray."), rotation: z .number() .min(0) .max(360) .optional() .describe( "Watermark rotation in degrees (0–360). Defaults to 45 (diagonal)." ), }) .strict(), annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false, }, }, async ({ filePath, text, outputPath, pages, opacity, fontSize, color, rotation, }) => { try { const resolvedPath = await validatePdfPath(filePath); await validateFileSize(resolvedPath); const resolvedOutput = await validateOutputPath(outputPath, filePath); const pdfDoc = await loadExistingPdf(resolvedPath); const font = await pdfDoc.embedFont(pdfLib.StandardFonts.Helvetica); const totalPages = pdfDoc.getPageCount(); const fSize = fontSize ?? 50; const opac = opacity ?? 0.3; const col = WATERMARK_COLORS[color ?? "gray"]; const rot = rotation ?? 45; let pageIndices: number[]; if (pages) { pageIndices = parsePageRange(pages, totalPages); } else { pageIndices = Array.from({ length: totalPages }, (_, i) => i); } for (const idx of pageIndices) { const page = pdfDoc.getPage(idx); const { width, height } = page.getSize(); const textWidth = font.widthOfTextAtSize(text, fSize); page.drawText(text, { x: (width - textWidth) / 2, y: height / 2, size: fSize, font, color: col, opacity: opac, rotate: pdfLib.degrees(rot), }); } await savePdf(pdfDoc, resolvedOutput); const fileSize = await getFileSize(resolvedOutput); const watermarkedLabel = pages ?? (totalPages === 1 ? "1" : `1-${totalPages}`); return toolSuccess({ outputPath: resolvedOutput, watermarkedPages: watermarkedLabel, text, fileSize, }); } catch (error) { return toolError( error instanceof Error ? error.message : String(error) ); } } );