# How to Add Custom Watermarks to Images Using ImageKit: A Complete Guide

> Easily add custom watermarks to images with ImageKit. Learn to use command-line flags or the Rust API for flexible watermarking solutions.

- Repository: [hzbd/imagekit](https://github.com/hzbd/imagekit)
- Tags: how-to-guide
- Published: 2026-03-03

---

**ImageKit enables adding custom watermarks to images through both a command-line interface with flags like `--watermark-text` and `--watermark-position`, and a Rust library API via the `add_watermark` function in [`src/processor.rs`](https://github.com/hzbd/imagekit/blob/main/src/processor.rs), supporting RGBA colors, nine positioning options, and automatic font scaling.**

ImageKit is a Rust-based open-source tool for batch image processing that includes a robust watermarking pipeline. Whether you need to brand a photo gallery or protect image assets, the library provides both CLI convenience and programmatic flexibility. According to the hzbd/imagekit source code, watermarking is implemented through a modular architecture that separates option parsing, font management, and image manipulation into distinct components.

## Understanding ImageKit's Watermark Architecture

The watermarking system in ImageKit follows a three-stage pipeline designed for parallel processing and thread safety.

### CLI Configuration and Color Parsing

In [`src/cli.rs`](https://github.com/hzbd/imagekit/blob/main/src/cli.rs), the `Cli` struct defines all watermark-related command-line arguments. Users can specify text content, position, font size, and color through flags like `--watermark-text`, `--watermark-position`, and `--watermark-color`. The file also implements the **HexColor** parser to handle RGBA color values (e.g., `#ffffff80` for semi-transparent white) and the **WatermarkPosition** enum with nine variants: `nw`, `north`, `ne`, `west`, `center`, `east`, `sw`, `south`, and `se`.

### Font Loading and Thread Pool Setup

The library entry point in [`src/lib.rs`](https://github.com/hzbd/imagekit/blob/main/src/lib.rs) loads three embedded fallback fonts—Roboto, Source Han Sans SC, and Noto Sans Thai—and stores them in an `Arc<Vec<Font>>`. This thread-safe reference counting allows the font data to be shared cheaply across Rayon's thread pool when processing multiple images concurrently.

### Image Processing and Watermark Rendering

The core logic resides in [`src/processor.rs`](https://github.com/hzbd/imagekit/blob/main/src/processor.rs). The `process_image` function handles optional resizing before calling `add_watermark` when text is provided. This function calculates appropriate scaling to prevent text overflow, invokes `layout_text` to rasterize glyphs using the fallback font stack, computes pixel offsets based on the selected position, and blends the final text onto the image using the specified RGBA color.

## Adding Watermarks via Command Line

The simplest way to add watermarks is using the pre-built binary. ImageKit supports batch processing entire directories while applying consistent watermark styling.

Example command for a semi-transparent watermark in the bottom-right corner:

```bash
./imagekit -i ./example/img-src -o ./example/img-out \
  --width 800 \
  --watermark-text "© My Brand" \
  --watermark-position se \
  --font-size 32 \
  --watermark-color "#ffffff80"

```

This command resizes images to 800px width, applies the text "© My Brand" at 32pt font size in the southeast (bottom-right) position, and sets the color to white with 50% opacity.

## Adding Watermarks Programmatically

For Rust applications, you can integrate ImageKit's watermarking directly using the library API. The `run` function accepts a `Cli` struct containing all watermark parameters.

```rust
use imagekit::{run, cli::Cli};
use std::path::PathBuf;

fn main() -> anyhow::Result<()> {
    let cli = Cli {
        input_dir: PathBuf::from("./example/img-src"),
        output_dir: PathBuf::from("./example/img-out"),
        width: Some(800),
        height: None,
        watermark_text: Some("@My Phone".into()),
        watermark_position: imagekit::cli::WatermarkPosition::Se,
        font_size: 24,
        watermark_color: imagekit::cli::HexColor(image::Rgba([255, 255, 255, 128])),
        quality: 85,
        output_format: None,
    };

    run(cli)
}

```

This approach allows dynamic configuration at runtime while leveraging the same parallel processing backend as the CLI.

## Customizing Watermark Appearance

ImageKit provides granular control over watermark positioning and styling through several key parameters.

**Position Options**: The `WatermarkPosition` enum supports nine anchor points: northwest (`nw`), north (top-center), northeast (`ne`), west (left-center), center, east (right-center), southwest (`sw`), south (bottom-center), and southeast (`se`). The library calculates pixel offsets automatically based on the rendered text's bounding box.

**Automatic Font Scaling**: In [`src/processor.rs`](https://github.com/hzbd/imagekit/blob/main/src/processor.rs), the `add_watermark` function includes logic to shrink text dynamically if the requested `font_size` would cause the watermark to overflow the image boundaries. This ensures watermarks remain visible and properly proportioned regardless of image dimensions.

**RGBA Color Support**: Colors are specified using the `HexColor` type, which parses standard hex codes with optional alpha channels. The format supports both 6-character RGB (e.g., `#ff0000`) and 8-character RGBA (e.g., `#ff000080` for 50% transparent red).

## Using Custom Fonts

While ImageKit includes three built-in fonts for international text support, you can substitute custom typefaces by interfacing directly with the processor module.

```rust
use rusttype::Font;
use imagekit::processor::add_watermark;
use image::{DynamicImage, Rgba};

fn custom_watermark(img: &mut DynamicImage) {
    let data = include_bytes!("MyFont.ttf") as &[u8];
    let font = Font::try_from_bytes(data).expect("invalid font data");

    add_watermark(
        img,
        "Custom Font Watermark",
        &[font],
        36,
        imagekit::cli::WatermarkPosition::Center,
        imagekit::cli::HexColor(Rgba([255, 0, 0, 200])),
    );
}

```

This example loads a custom TTF file and calls `add_watermark` directly with a single-font slice, bypassing the default font stack loaded in [`src/lib.rs`](https://github.com/hzbd/imagekit/blob/main/src/lib.rs).

## Summary

- **CLI Integration**: Use flags like `--watermark-text`, `--watermark-position`, and `--watermark-color` in [`src/cli.rs`](https://github.com/hzbd/imagekit/blob/main/src/cli.rs) for quick batch processing.
- **Library API**: Import `imagekit::run` and construct a `Cli` struct for programmatic control within Rust applications.
- **Parallel Processing**: The system uses Rayon and `Arc<Vec<Font>>` in [`src/lib.rs`](https://github.com/hzbd/imagekit/blob/main/src/lib.rs) for efficient multi-threaded watermarking of image batches.
- **Flexible Positioning**: Nine position variants (nw, north, ne, west, center, east, sw, south, se) are calculated automatically in [`src/processor.rs`](https://github.com/hzbd/imagekit/blob/main/src/processor.rs).
- **Smart Rendering**: The `add_watermark` function auto-scales text to fit image bounds and supports RGBA transparency via `HexColor`.

## Frequently Asked Questions

### What watermark positions does ImageKit support?

ImageKit supports nine positioning options defined in [`src/cli.rs`](https://github.com/hzbd/imagekit/blob/main/src/cli.rs): northwest (`nw`), north (`north`), northeast (`ne`), west (`west`), center (`center`), east (`east`), southwest (`sw`), south (`south`), and southeast (`se`). The `add_watermark` function in [`src/processor.rs`](https://github.com/hzbd/imagekit/blob/main/src/processor.rs) calculates the exact pixel coordinates based on the text bounding box and selected anchor point.

### How does ImageKit handle fonts for international text?

The library loads three fallback fonts in [`src/lib.rs`](https://github.com/hzbd/imagekit/blob/main/src/lib.rs): Roboto for Latin scripts, Source Han Sans SC for Chinese characters, and Noto Sans Thai for Thai scripts. These are stored in an `Arc<Vec<Font>>` and shared across threads, with the `layout_text` function in [`src/processor.rs`](https://github.com/hzbd/imagekit/blob/main/src/processor.rs) attempting each font in sequence until it finds one that can render the requested characters.

### Can I make the watermark semi-transparent?

Yes. The `HexColor` parser in [`src/cli.rs`](https://github.com/hzbd/imagekit/blob/main/src/cli.rs) supports 8-character hex codes with alpha channels (e.g., `#ffffff80` for 50% opacity white). When rendering, the `add_watermark` function respects the alpha value when blending glyphs onto the underlying image in [`src/processor.rs`](https://github.com/hzbd/imagekit/blob/main/src/processor.rs).

### Does ImageKit prevent watermarks from overflowing small images?

Yes. The `add_watermark` function in [`src/processor.rs`](https://github.com/hzbd/imagekit/blob/main/src/processor.rs) (lines 38-52) includes automatic scaling logic that detects when the requested `font_size` would cause text to exceed image boundaries. It proportionally shrinks the text to ensure the watermark remains fully visible regardless of image dimensions.