Skip to main content
Glama

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
NameRequiredDescriptionDefault
filePathYesAbsolute path to the source PDF file
textYesWatermark text to overlay on pages
outputPathYesAbsolute path for the watermarked output PDF
pagesNoPage range, e.g. '1-5' or '1,3,5'. Omit to watermark all pages.
opacityNoWatermark opacity (0.0–1.0). Defaults to 0.3.
fontSizeNoWatermark font size (10–200). Defaults to 50.
colorNoWatermark color. Defaults to gray.
rotationNoWatermark rotation in degrees (0–360). Defaults to 45 (diagonal).

Implementation Reference

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

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/AryanBV/pdf-toolkit-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server