Add watermarks, stamps, headers and page numbers

This guide shows developers how to use PDFluent to apply common page elements to PDF documents. It is for Rust programmers working with PDFs.

Add PDFluent to your project

Add the pdfluent crate to Cargo.toml.

toml
[dependencies]
pdfluent = "1.0.0-beta.18"

Add a text watermark to a PDF in Rust

Stamp each page with a diagonal text watermark. Control font, size, colour, opacity, rotation, and position.

rust
use pdfluent::{PdfDocument, WatermarkOptions};

fn main() -> pdfluent::Result<()> {
    let mut doc = PdfDocument::open("report.pdf")?;
    doc.add_watermark("DRAFT", WatermarkOptions::centered().opacity(0.3))?;
    doc.save("watermarked.pdf")?;
    Ok(())
}
  1. Open the PDF

    Open the document with a mutable binding.

    rust
    use pdfluent::PdfDocument;
    
    let mut doc = PdfDocument::open("contract.pdf")?;
  2. Build a TextWatermark

    Create a TextWatermark with the text you want to stamp. Use the builder methods to set style properties.

    rust
    use pdfluent::WatermarkOptions;
    
    let opts = WatermarkOptions::centered()
        .font_size(72.0)
        .opacity(0.12)          // 12% opacity
        .rotated(45.0)          // degrees
        .color(0.6, 0.0, 0.0);  // dark red, sRGB
  3. Set the position

    WatermarkPosition::Center places the text at the page centre. Other options include TopLeft, TopRight, BottomLeft, BottomRight, and Custom(x, y).

    rust
    // Watermarks are centered by default; tune size, angle, and opacity
    let opts = WatermarkOptions::centered()
        .font_size(64.0)
        .rotated(45.0)
        .opacity(0.15);
  4. Apply to all pages or specific pages

    add_text_watermark() stamps every page. Use add_text_watermark_on_pages() to target a subset.

    rust
    // Apply to all pages
    doc.add_watermark("DRAFT", opts)?;
  5. Save the result

    Save the watermarked document to disk.

    rust
    doc.save("contract_draft.pdf")?;
  • Opacity of 0.1 to 0.2 is typical for background watermarks. Higher values produce more prominent stamps.
  • The watermark is drawn as a content stream beneath the page text so it does not obscure the document content.
  • To place the watermark on top of the content instead, use watermark.layer(WatermarkLayer::Foreground).
  • PDFluent uses the Helvetica built-in font by default. Specify a custom font path with .font_path("path/to/font.ttf").

Add an image watermark to a PDF in Rust

Overlay a logo or stamp image on every page of a PDF. Control position, size, opacity, and rotation.

rust
use pdfluent::{PdfDocument, ImageWatermark, WatermarkPosition};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut doc = PdfDocument::open("document.pdf")?;

    let watermark = ImageWatermark::from_file("logo.png")?
        .opacity(0.10)
        .width_percent(40.0)
        .position(WatermarkPosition::Center);

    doc.add_image_watermark(&watermark)?;
    doc.save("document_branded.pdf")?;
    Ok(())
}
  1. Open the PDF

    Open the document with a mutable binding.

    rust
    use pdfluent::PdfDocument;
    
    let mut doc = PdfDocument::open("brochure.pdf")?;
  2. Load the watermark image

    PDFluent accepts PNG and JPEG files. PNG with an alpha channel is supported, so transparent logos blend cleanly.

    rust
    use pdfluent::ImageWatermark;
    
    let watermark = ImageWatermark::from_file("company_logo.png")?;
  3. Configure size and opacity

    Set the width as a percentage of the page width. The height is calculated automatically to preserve the image aspect ratio.

    rust
    let watermark = watermark
        .opacity(0.15)          // 15% opacity
        .width_percent(30.0)    // 30% of page width
        .rotation(0.0);         // no rotation
  4. Set the position

    Choose from preset positions or provide exact coordinates in PDF points with WatermarkPosition::Custom(x, y).

    rust
    use pdfluent::WatermarkPosition;
    
    let watermark = watermark
        .position(WatermarkPosition::BottomRight);
  5. Apply and save

    Stamp all pages and write the result to disk.

    rust
    doc.add_image_watermark(&watermark)?;
    doc.save("brochure_branded.pdf")?;
  • PNG watermarks with a transparent background blend correctly over page content.
  • JPEG watermarks always have a white background because JPEG does not support transparency.
  • Large high-resolution images increase the PDF file size. Resize the watermark image to a reasonable resolution (72-150 DPI at intended size) before adding it.
  • The watermark image is embedded once and referenced on each page, so file size growth is minimal even for multi-page documents.

Apply a stamp or label to each page of a PDF in Rust

Draw text or an image stamp at a fixed position on every page, with configurable opacity, size, and rotation.

rust
use pdfluent::{PdfDocument, stamp::{TextStamp, StampPosition}};

fn main() -> pdfluent::Result<()> {
    let mut doc = PdfDocument::open("input.pdf")?;

    let stamp = TextStamp::new("CONFIDENTIAL")
        .position(StampPosition::Center)
        .font_size(48.0)
        .opacity(0.25)
        .rotation(45.0)
        .color(pdfluent::color::Color::rgb(0.8, 0.0, 0.0));

    doc.apply_stamp(&stamp)?;
    doc.save("stamped.pdf")?;
    Ok(())
}
  1. Open the document

    Stamps are applied to page content streams. Open the file for mutation.

    rust
    let mut doc = PdfDocument::open("input.pdf")?;
  2. Build a text stamp

    TextStamp::new takes the stamp text. Chain builder methods for position, size, color, rotation, and opacity.

    rust
    use pdfluent::stamp::{TextStamp, StampPosition};
    
    let stamp = TextStamp::new("DRAFT")
        .position(StampPosition::Center)
        .font_size(72.0)
        .opacity(0.15)
        .rotation(45.0);
  3. Apply the stamp to all pages

    doc.apply_stamp() iterates all pages and appends the stamp as a graphics operator block in each content stream.

    rust
    doc.apply_stamp(&stamp)?;
  4. Apply a stamp to specific pages only

    Use apply_stamp_to_page to target individual pages.

    rust
    // Stamp only pages 1 and 3 (zero-indexed: 0, 2)
    doc.apply_stamp_to_page(&stamp, 0)?;
    doc.apply_stamp_to_page(&stamp, 2)?;
  5. Apply an image stamp

    Load a PNG or JPEG and use ImageStamp to overlay it on each page.

    rust
    use pdfluent::stamp::{ImageStamp, StampPosition};
    
    let image_data = std::fs::read("stamp_logo.png")?;
    let img_stamp = ImageStamp::from_png(&image_data)
        .position(StampPosition::BottomRight)
        .width_pt(120.0)
        .opacity(0.6);
    
    doc.apply_stamp(&img_stamp)?;
    doc.save("stamped.pdf")?;
  • Stamps are written into the content stream as a transparency group with a gs operator. They cannot be trivially removed without reprocessing the stream.
  • For removable stamps, add them as annotation objects instead. Annotation-based stamps can be toggled or deleted without modifying the content stream.
  • Very low opacity (below 0.1) may not be visible in all viewers. Use 0.15-0.25 for a visible but unobtrusive stamp.
  • Rotate the stamp 45 degrees for a diagonal diagonal watermark-style placement. 0 degrees places the text horizontally.

Add page numbers to a PDF in Rust

Stamp page numbers onto every page of a PDF. Control position, font, format, and starting number. Skip the cover page or use Roman numerals for front matter.

rust
use pdfluent::{PdfDocument, PageNumberOptions, PageNumberPosition};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut doc = PdfDocument::open("report.pdf")?;

    doc.add_page_numbers(
        PageNumberOptions::default()
            .position(PageNumberPosition::BottomCenter)
            .format("{page} / {total}")
            .font_size(9.0)
            .start_page(1)   // skip cover page (index 0)
            .start_number(1),
    )?;

    doc.save("report_numbered.pdf")?;
    Ok(())
}
  1. Open the PDF

    Load the document you want to number.

    rust
    use pdfluent::PdfDocument;
    
    let mut doc = PdfDocument::open("thesis.pdf")?;
    println!("Pages: {}", doc.page_count());
  2. Configure page number options

    PageNumberOptions controls position, font, format string, and which pages receive numbers.

    rust
    use pdfluent::{PageNumberOptions, PageNumberPosition};
    
    let opts = PageNumberOptions::default()
        .position(PageNumberPosition::BottomCenter)
        .format("{page}")         // or "Page {page} of {total}"
        .font("Helvetica")
        .font_size(10.0)
        .margin(30.0)             // distance from page edge in points
        .start_page(0)            // zero-based index of first numbered page
        .start_number(1);         // the number printed on start_page
  3. Use Roman numerals for front matter

    Apply Roman numeral numbering to the first few pages, then switch to Arabic for the main body.

    rust
    use pdfluent::{PageNumberOptions, PageNumberPosition, NumberStyle};
    
    // Front matter: pages 0-3 in Roman numerals (i, ii, iii, iv)
    let front_opts = PageNumberOptions::default()
        .position(PageNumberPosition::BottomCenter)
        .style(NumberStyle::RomanLower)
        .start_page(0)
        .end_page(3)
        .start_number(1)
        .font_size(9.0);
    
    // Main body: pages 4 onward in Arabic (1, 2, 3, ...)
    let body_opts = PageNumberOptions::default()
        .position(PageNumberPosition::BottomCenter)
        .style(NumberStyle::Arabic)
        .start_page(4)
        .start_number(1)
        .font_size(9.0);
    
    doc.add_page_numbers(front_opts)?;
    doc.add_page_numbers(body_opts)?;
  4. Save the numbered document

    Write the result to disk.

    rust
    doc.save("thesis_numbered.pdf")?;
    println!("Page numbers added.");
  • Page numbers are stamped directly onto the content stream. They are permanent and visible to all viewers.
  • start_page is a zero-based index. To skip a cover page, set start_page(1) so page index 0 is left unnumbered.
  • The format string supports {page} for the current page number and {total} for the total page count.
  • margin controls the distance from the nearest page edge. Increase it if numbers appear clipped.