Watermark (1)
PDF has a fixed layout and is easy to distribute, but once its content has been generated, it is difficult to modify within the body text. For documents such as contracts, reports, and notices, you often need to indicate the confidentiality level, copyright ownership, or usage states such as "Draft" and "Sample" without affecting the reading of the body content. A text watermark is a common solution to this problem: it floats over the content as semi-transparent text, conveying the information clearly without harming the readability of the original.
Spire.PDF for JavaScript loads, draws, and saves PDFs directly in the browser via WebAssembly, managing input and output files through a virtual file system (VFS) without requiring a backend server. Text watermarking usually takes two forms: one places a single line of watermark text diagonally across the center of each page, which can be achieved directly through the transparency settings and coordinate-system transformations of the page canvas; the other tiles text repeatedly across the whole page, which can be done with the PdfTilingBrush tiling brush.
This article covers two core features:
For installation and project setup, refer to Integrating Spire.PDF for JavaScript in a React Project. The examples below assume Spire.PDF is installed and the WebAssembly module is initialized.
Add a Single-Line Text Watermark to PDF
A single-line text watermark places a line of diagonal text at the center of each page, suitable for marking confidentiality levels or copyright ownership. The approach is as follows: use a PdfTrueTypeFont based on a font that supports the characters you need, together with MeasureString, to measure the text size and compute the centering offset; then, page by page, set the transparency and rotate the coordinate system through SetTransparency, TranslateTransform, and RotateTransform; and finally draw the watermark text with DrawString.
function App() {
const addSingleLineTextWatermark = async () => {
// Get the Spire.PDF WASM module
const pdfModule = window.wasmModule?.spirepdf;
// Check whether the WASM module is ready
if (!pdfModule) {
alert('Spire.PDF is not ready yet');
return;
}
// Load a TrueType font into VFS
await window.spire.FetchFileToVFS('ARIAL UNICODE MS.TTF', '/Library/Fonts/', `${process.env.PUBLIC_URL}/font/`);
// Load the PDF file to be watermarked into VFS
const inputFileName = 'Lease_Agreement_EN.pdf';
await window.spire.FetchFileToVFS(inputFileName, "", `${process.env.PUBLIC_URL}/data/`);
// Create a PdfDocument object and load the PDF document
let doc = new pdfModule.PdfDocument();
doc.LoadFromFile(inputFileName);
// Create a TrueType font: bold, 30 point
let trueTypeFont = new pdfModule.PdfTrueTypeFont({
fontFamily: 'Arial Unicode MS',
size: 30,
style: pdfModule.PdfFontStyle.Bold,
unicode: true
});
// Create the watermark brush and specify the watermark text
let brush = pdfModule.PdfBrushes.get_DarkGray();
const text = 'CONFIDENTIAL - DO NOT DISCLOSE';
// Measure the size of the watermark text
let textSize = trueTypeFont.MeasureString({ text: text });
// Compute two offsets to determine the coordinate translation, so that the watermark is centered diagonally
let offset1 = (textSize.Width * Math.sqrt(2)) / 4;
let offset2 = (textSize.Height * Math.sqrt(2)) / 4;
let format = new pdfModule.PdfStringFormat({ alignment: pdfModule.PdfTextAlignment.Left });
// Loop through all the pages in the document
for (let i = 0; i < doc.Pages.Count; i++) {
// Get the specified page
let page = doc.Pages.get_Item(i);
// Set the page transparency
page.Canvas.SetTransparency(0.8);
// Translate the coordinate system to the center of the page and compensate for the offset caused by the text size
page.Canvas.TranslateTransform(
page.Canvas.ClientSize.Width / 2 - offset1 - offset2,
page.Canvas.ClientSize.Height / 2 + offset1 - offset2
);
// Rotate the coordinate system counterclockwise by 45 degrees
page.Canvas.RotateTransform({ angle: -45 });
// Draw the watermark text on the page
page.Canvas.DrawString({ s: text, font: trueTypeFont, brush: brush, x: 0, y: 0, format: format });
}
// Define the output file name and save the document
const outputFileName = 'SingleLineTextWatermark.pdf';
doc.SaveToFile(outputFileName);
doc.Close();
// Read the generated file from VFS and trigger the download
const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const blob = new Blob([fileArray], { type: 'application/pdf' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = outputFileName;
a.click();
URL.revokeObjectURL(url);
};
return (
<div style={{ textAlign: 'center', height: '300px' }}>
<h1>Add Single-line Text Watermark To PDF</h1>
<button onClick={addSingleLineTextWatermark}>
Generate
</button>
</div>
);
}
export default App;
PDF document after adding the single-line text watermark

Add a Multiline Text Watermark to PDF
When you need the watermark to fill the entire page, use a multiline text watermark. The approach is as follows: use PdfTilingBrush to divide the page into tiling cells according to the page size; inside a cell, adjust the transparency and angle with SetTransparency and RotateTransform and draw the text with DrawString; finally fill the whole page with the brush using DrawRectangle.
function App() {
const addMultilineTextWatermark = async () => {
// Get the Spire.PDF WASM module
const pdfModule = window.wasmModule?.spirepdf;
// Check whether the WASM module is ready
if (!pdfModule) {
alert('Spire.PDF is not ready yet');
return;
}
// Load the PDF file to be watermarked into VFS
const inputFileName = 'Lease_Agreement_EN.pdf';
await window.spire.FetchFileToVFS(inputFileName, "", `${process.env.PUBLIC_URL}/data/`);
// Create a PdfDocument object and load the PDF document
let doc = new pdfModule.PdfDocument();
doc.LoadFromFile(inputFileName);
// Get the first page of the document
let page = doc.Pages.get_Item(0);
// Create a tiling brush: use half the page width and one third of the page height as the tiling cell
let size = new pdfModule.SizeF({
width: page.Canvas.ClientSize.Width / 2,
height: page.Canvas.ClientSize.Height / 3
});
let brush = new pdfModule.PdfTilingBrush({ size: size });
// Set the watermark transparency to 30%
brush.Graphics.SetTransparency(0.3);
// Save the current state of the brush, then translate and rotate the coordinate system so that the watermark is arranged diagonally
brush.Graphics.Save();
brush.Graphics.TranslateTransform(brush.Size.Width / 2, brush.Size.Height / 2);
brush.Graphics.RotateTransform({ angle: -45 });
// Draw the tiled watermark
let format = new pdfModule.PdfStringFormat({ alignment: pdfModule.PdfTextAlignment.Center });
// Create font: bold, 25 point
let font = new pdfModule.PdfFont({ fontFamily: pdfModule.PdfFontFamily.Helvetica, size: 25 });
// Draw the watermark text
brush.Graphics.DrawString({
s: "CONFIDENTIAL",
font: font,
brush: pdfModule.PdfBrushes.get_DarkRed(),
x: 0,
y: -18,
format: format
});
// Restore the previous state of the brush and set it back to opaque
brush.Graphics.Restore();
brush.Graphics.SetTransparency({ alpha: 1 });
// Fill a whole-page rectangle with the tiling brush so that the watermark text tiles across the entire page
let rect = new pdfModule.RectangleF({
location: new pdfModule.PointF(0, 0),
size: page.Canvas.ClientSize
});
page.Canvas.DrawRectangle({ brush: brush, rectangle: rect });
// Define the output file name and save the document
const outputFileName = 'MultilineTextWatermark.pdf';
doc.SaveToFile(outputFileName);
doc.Close();
// Read the generated file from VFS and trigger the download
const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const blob = new Blob([fileArray], { type: 'application/pdf' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = outputFileName;
a.click();
URL.revokeObjectURL(url);
};
return (
<div style={{ textAlign: 'center', height: '300px' }}>
<h1>Add Multiline Text Watermark To PDF</h1>
<button onClick={addMultilineTextWatermark}>
Generate
</button>
</div>
);
}
export default App;
PDF document after adding the multiline text watermark

FAQ
How to set the font, size, and color of the watermark text
Reason: DrawString requires you to explicitly specify the font and brush used to draw the text.
Solution: The font, size, and color of the watermark text are determined by the font and brush passed to DrawString. For Latin text, create a PdfFont based on a built-in PdfFontFamily such as Helvetica, and set the color through the brush:
// Create a built-in font: Helvetica, 24 point
let font = new pdfModule.PdfFont({
fontFamily: pdfModule.PdfFontFamily.Helvetica,
size: 24
});
// Set the watermark color through the brush
let brush = pdfModule.PdfBrushes.get_DarkRed();
// Draw the watermark text on the page canvas
page.Canvas.DrawString({ s: 'CONFIDENTIAL', font: font, brush: brush, x: 0, y: 0, format: format });
If you need a font that is not built in — for example, to display non-Latin scripts such as Chinese or Japanese, or to apply a specific typeface — load the corresponding TrueType font into VFS and use PdfTrueTypeFont instead, as shown in the single-line text watermark example.
How to add a watermark to every page of a PDF
Reason: In the single-line text watermark example, a page loop applies the watermark to every page, while the multiline text watermark example only targets the first page through doc.Pages.get_Item(0).
Solution: To make the multiline watermark cover the entire document as well, move the creation of the tiling brush and the page fill into the page loop:
for (let i = 0; i < doc.Pages.Count; i++) {
let page = doc.Pages.get_Item(i);
// Create a tiling brush and set the transparency, rotation, and text
let size = new pdfModule.SizeF({
width: page.Canvas.ClientSize.Width / 2,
height: page.Canvas.ClientSize.Height / 3
});
let brush = new pdfModule.PdfTilingBrush({ size: size });
// …… set transparency, rotate, and draw the watermark text ……
// Fill the current page with the tiling brush
page.Canvas.DrawRectangle({
brush: brush,
rectangle: new pdfModule.RectangleF({ location: new pdfModule.PointF(0, 0), size: page.Canvas.ClientSize })
});
}
How to control the transparency and rotation angle of the watermark
Reason: Too high or too low transparency affects the appearance of the watermark, and the rotation angle determines the direction of the watermark text.
Solution: Use SetTransparency to set the transparency, whose value ranges from 0 (fully transparent) to 1 (opaque); use RotateTransform to control the coordinate-system rotation angle, where a negative value means counterclockwise rotation. The single-line example sets the transparency to 0.8 and rotates by -45 degrees, and the multiline tiling example makes the same settings within the graphics context of the tiling brush:
// Single-line watermark: set the page transparency and rotate the page canvas
page.Canvas.SetTransparency(0.8);
page.Canvas.RotateTransform({ angle: -45 });
// Multiline tiled watermark: set the transparency and rotation within the graphics context of the tiling brush
brush.Graphics.SetTransparency(0.3);
brush.Graphics.RotateTransform({ angle: -45 });
Get a Free License
If you want to remove the evaluation messages in the resulting documents or get rid of functional limitations, contact sales to obtain a 30-day temporary license.