# How ImageKit Performs Intelligent Watermark Scaling: Algorithm and Implementation

> Discover how ImageKit automatically scales watermark text to fit any image. Learn the intelligent algorithm for perfect watermark placement and seamless integration.

- Repository: [hzbd/imagekit](https://github.com/hzbd/imagekit)
- Tags: deep-dive
- Published: 2026-03-03

---

**ImageKit automatically scales down watermark text to fit within image boundaries by calculating width and height ratios and applying the smaller scale factor, ensuring the watermark never overflows regardless of font size or text length.**

ImageKit is an open-source Rust image processing toolkit that handles dynamic watermarking with automatic size adjustment. The library implements intelligent watermark scaling in the `add_watermark` function within [`src/processor.rs`](https://github.com/hzbd/imagekit/blob/main/src/processor.rs), preserving user-requested dimensions when possible while preventing text overflow through proportional reduction.

## How Intelligent Watermark Scaling Works

The scaling algorithm follows a six-step process that balances user preferences with image constraints. As implemented in [`src/processor.rs`](https://github.com/hzbd/imagekit/blob/main/src/processor.rs), the logic ensures watermarks remain readable without exceeding the canvas boundaries.

### Step 1: Determine the Drawable Area with Padding

First, the function retrieves the image dimensions using `img.dimensions()`. It then subtracts a constant **10-pixel padding** from each side to create a safe drawable region. This ensures the watermark does not touch the absolute edge of the image.

```rust
// Lines 40-42 in src/processor.rs
let (img_width, img_height) = img.dimensions();
let max_drawable_width = img_width - 10;
let max_drawable_height = img_height - 10;

```

### Step 2: Measure Text at the Requested Font Size

The helper function `layout_text` calculates the bounding box of the supplied text using the user-specified font size. This initial measurement determines whether the text fits naturally or requires compression.

```rust
// Lines 43-44: Initial measurement
let (text_width, text_height) = layout_text(text, fonts, Scale::uniform(font_size));

```

### Step 3: Detect Overflow and Calculate Scaling Ratios

If the measured text exceeds the drawable area in either dimension, the algorithm computes two scaling ratios: one for width and one for height. The code selects the smaller ratio to ensure the text fits within both constraints simultaneously.

```rust
// Lines 45-48: Ratio calculation
let width_ratio = max_drawable_width as f32 / text_width as f32;
let height_ratio = max_drawable_height as f32 / text_height as f32;
let scale_factor = width_ratio.min(height_ratio);

```

### Step 4: Apply the Minimum Scale Factor

The new font size equals the original size multiplied by `scale_factor`. The implementation floors this value and clamps it to a minimum of **1 pixel**, preventing invalid zero or negative sizes.

```rust
// Lines 49-51: Apply scaling
let new_font_size = (font_size as f32 * scale_factor).floor() as u32;
let new_font_size = new_font_size.max(1);
let scale = Scale::uniform(new_font_size as f32);

```

### Step 5: Re-layout and Render

Finally, the system invokes `layout_text` again with the adjusted `scale` (line 53) to generate the glyph positions. The glyphs are then drawn at the chosen position, guaranteeing the watermark fits entirely within the padded boundaries.

## Implementation Details in src/processor.rs

The core logic resides in the `add_watermark` function, which coordinates between the CLI arguments defined in [`src/cli.rs`](https://github.com/hzbd/imagekit/blob/main/src/cli.rs) and the rendering pipeline. The function accepts parameters including `font_size`, `WatermarkPosition`, and `HexColor`, but overrides the size when overflow is detected.

Key architectural components include:

- **`layout_text`**: A private helper that computes text dimensions using the `rusttype` crate.
- **Padding constant**: Hardcoded at 10px to maintain visual margin regardless of image resolution.
- **Scale clamping**: Ensures the font never collapses to zero, maintaining at least 1px height for visibility.

## Usage Examples

You can trigger intelligent scaling both via the command line and programmatically. In both cases, the API accepts a requested font size, but the final rendered size may differ based on image dimensions.

### Command-Line Interface

When using the `imagekit` CLI, simply specify your desired font size. The tool automatically reduces the size if the text would overflow the target image.

```bash

# Request 72pt text on a small image

imagekit -i example/img-src/large-photo.jpg \
         -o example/img-out/ \
         --watermark-text "© 2026 MyCompany" \
         --font-size 72 \
         --watermark-position se

```

*Result*: The watermark renders at a reduced size (e.g., 38pt) to fit within the padded drawable area.

### Rust Programmatic API

When calling `add_watermark` directly, pass your preferred size as an argument. The function handles scaling internally using the algorithm described above.

```rust
use imagekit::processor::add_watermark;
use imagekit::cli::{HexColor, WatermarkPosition};
use rusttype::Font;
use image::DynamicImage;

let mut img = image::open("input.png")?;
let font_data = std::fs::read("assets/Roboto-Regular.ttf")?;
let font = Font::try_from_vec(font_data).unwrap();

// Request 80pt; actual size may be smaller
add_watermark(
    &mut img,
    "Confidential",
    &[font],
    80, // requested_font_size
    WatermarkPosition::South,
    HexColor::from_rgba(255, 255, 255, 128),
)?;
img.save("output.png")?;

```

## Summary

- **Intelligent watermark scaling** in ImageKit automatically adjusts font size to prevent text overflow.
- The algorithm calculates `width_ratio` and `height_ratio`, applying the minimum value as a `scale_factor`.
- A constant **10px padding** ensures watermarks maintain a safe margin from image edges.
- The implementation in [`src/processor.rs`](https://github.com/hzbd/imagekit/blob/main/src/processor.rs) (lines 40-53) preserves the requested size when possible, only scaling down when necessary.
- Both CLI and programmatic APIs support this feature transparently, requiring no manual size calculations from the user.

## Frequently Asked Questions

### How does ImageKit determine when to scale a watermark down?

ImageKit compares the rendered text dimensions against the drawable area (image size minus 10px padding). If either the `text_width` exceeds `max_drawable_width` or `text_height` exceeds `max_drawable_height`, the system calculates scaling ratios and reduces the font size proportionally using the smaller ratio to ensure a perfect fit.

### What is the minimum font size ImageKit will render for a watermark?

The algorithm clamps the scaled font size to a minimum of **1 pixel** using `.max(1)`. This prevents invalid rendering attempts while ensuring the watermark remains technically visible, even on extremely small images.

### Can I disable automatic watermark scaling in ImageKit?

No, the intelligent scaling behavior is built into the `add_watermark` function in [`src/processor.rs`](https://github.com/hzbd/imagekit/blob/main/src/processor.rs) and cannot be disabled via configuration flags. The algorithm always enforces the constraint that watermarks must fit within the padded image boundaries to prevent partial or clipped text rendering.

### Which Rust crates does ImageKit use for text measurement and rendering?

ImageKit relies on the **`rusttype`** crate for font parsing and glyph layout, the **`image`** crate for raster operations, and **`anyhow`** for error handling. These dependencies are declared in [`Cargo.toml`](https://github.com/hzbd/imagekit/blob/main/Cargo.toml) and power the `layout_text` helper function that enables accurate dimension calculations required for intelligent scaling.