Packages

A Plug-based image optimization server with a declarative path API.

Current section

Files

Jump to
image_pipe docs processing watermark.md
Raw

docs/processing/watermark.md

# Watermarks

Watermark options draw an image, such as a logo, over the result of a
[group](../requesting-images.md#processing-groups).

The watermark is the last step of its group, after effects, canvas, padding,
and background, so it covers the whole frame, padding included (see
[processing order](../processing.md#processing-order)). Each group starts
without a watermark. The other `wm-*` options need [`wm`](#wm),
[`wm-src64`, or `wm-enc`](#wm-src64-and-wm-enc) in the same group, and are
[ignored](../requesting-images.md#ignored-options) without one.

<!-- tabs-open -->

### URL

```text
/w=800/wm=logo/wm-at=bottom-right/wm-offset=16,16/wm-scale=0.2/src/photos/beach.jpg
```

### Elixir

```elixir
ImagePipe.URL.new()
|> ImagePipe.URL.group(
  resize: [width: 800],
  watermark: :logo,
  watermark_at: :bottom_right,
  watermark_offset: {16, 16},
  watermark_scale: 0.2
)
```

<!-- tabs-close -->

## Watermark assets

### wm

Accepts the name of a watermark defined in the server's configuration:
lowercase letters, digits, `_`, and `-`. Default: no watermark.

Draws that named image. The server's configuration defines which names
exist. A name it doesn't define fails with `400` and `unknown watermark`. The watermark
may come with its own opacity, which [`wm-opacity`](#wm-opacity) multiplies.

<!-- tabs-open -->

### URL

```text
/wm=logo/src/photos/beach.jpg
```

### Elixir

```elixir
ImagePipe.URL.group(builder, watermark: :logo)
```

<!-- tabs-close -->

### wm-src64 and wm-enc

`wm-src64` accepts an image path in unpadded base64url, written the same way as
after the [`src64/` marker](../requesting-images.md#image-paths). `wm-enc`
accepts an encrypted token, like the `enc/` marker. These tokens need the
server's encryption key, so they are created server-side, for example by the
application that builds your URLs. Default: no watermark.

Draws the image at that path, from the same image sources as the main image.
A `wm-enc` token that no server key decrypts answers `404`. A server without
source encryption keys answers `400` for every `wm-enc`.

A group takes one watermark: `wm`, `wm-src64`, and `wm-enc` can't be combined,
and using two fails with `400`, such as `wm and wm-src64 are mutually exclusive`.

<!-- tabs-open -->

### URL

```text
/wm-src64=YnJhbmQvbG9nby5wbmc/src/photos/beach.jpg
```

### Elixir

```elixir
ImagePipe.URL.group(builder, watermark_source: "brand/logo.png")
```

<!-- tabs-close -->

Watermark names, and whether requests may name their own watermarks, are set
in the [Plug configuration](`ImagePipe.config/1`) or the
[server configuration](../../../image_pipe_server/docs/server-configuration.md#processing).

## Size and placement

### wm-opacity

Accepts a [fraction](../requesting-images.md#fractions). Default: `1`.

Sets how opaque the watermark is. The value multiplies any opacity the named
watermark already has, so `wm-opacity=0.5` on a watermark defined at `0.6`
draws it at `0.3`. `wm-opacity=0` draws nothing, and the watermark image isn't
fetched.

<!-- tabs-open -->

### URL

```text
/wm=logo/wm-opacity=0.5/src/photos/beach.jpg
```

### Elixir

```elixir
ImagePipe.URL.group(builder, watermark: :logo, watermark_opacity: 0.5)
```

<!-- tabs-close -->

### wm-scale

Accepts a number greater than `0` and at most `1`. Default: the watermark's own
size, multiplied by the group's [`dpr`](resize.md#dpr).

Fits the watermark inside that fraction of the frame's width and height,
keeping its aspect ratio. With `wm-scale=0.25` on an 800×600 result, a
watermark fits inside 200×150. A small watermark is enlarged to fit, so a
raster logo can look soft. An SVG watermark renders at the size it's drawn
and stays sharp (see [Serving SVG images](../serving-svg-images.md)).

<!-- tabs-open -->

### URL

```text
/w=800/wm=logo/wm-scale=0.25/src/photos/beach.jpg
```

### Elixir

```elixir
ImagePipe.URL.group(builder, resize: [width: 800], watermark: :logo, watermark_scale: 0.25)
```

<!-- tabs-close -->

### wm-at

Accepts an [anchor](../requesting-images.md#anchors). Default: `center`.

Places the watermark at that position in the frame.

<!-- tabs-open -->

### URL

```text
/wm=logo/wm-at=bottom-right/src/photos/beach.jpg
```

### Elixir

```elixir
ImagePipe.URL.group(builder, watermark: :logo, watermark_at: :bottom_right)
```

<!-- tabs-close -->

### wm-offset

Accepts `x,y`, each a [pixel length](../requesting-images.md#pixel-lengths) or
a [percentage](../requesting-images.md#percentages), and either may be
negative. Default: `0,0`.

Moves the watermark from its [`wm-at`](#wm-at) position:

- With a `right` or `bottom` anchor, positive values move it inward, away from
  that edge.
- With a `left`, `top`, or `center` anchor, positive values move it right or
  down.

Pixels are multiplied by the group's `dpr`. A percentage `x` is of the frame
width, and a percentage `y` of the frame height. The offset isn't limited to
the frame: the part of the watermark outside it is cut off, and a watermark
moved completely outside draws nothing.

<!-- tabs-open -->

### URL

```text
/wm=logo/wm-at=bottom-right/wm-offset=16,5pct/src/photos/beach.jpg
```

### Elixir

```elixir
ImagePipe.URL.group(builder,
  watermark: :logo,
  watermark_at: :bottom_right,
  watermark_offset: {16, {:pct, 5}}
)
```

<!-- tabs-close -->

### wm-tile

A [boolean](../requesting-images.md#booleans). Default: `false`.

Repeats the watermark across the whole frame. One copy sits where the single
watermark would be, after [`wm-at`](#wm-at) and [`wm-offset`](#wm-offset), and
the copies repeat from there in every direction.

<!-- tabs-open -->

### URL

```text
/wm=logo/wm-tile/wm-scale=0.1/src/photos/beach.jpg
```

### Elixir

```elixir
ImagePipe.URL.group(builder, watermark: :logo, watermark_tile: true, watermark_scale: 0.1)
```

<!-- tabs-close -->

### wm-gap

Accepts `x,y`, each a pixel length or a percentage, not negative. Default:
`0,0`.

Adds space to the right of and below each tiled copy. Pixels are multiplied by
the group's `dpr`, and percentages are of the frame width (`x`) and height
(`y`). Requires [`wm-tile`](#wm-tile), and is ignored without it.

<!-- tabs-open -->

### URL

```text
/wm=logo/wm-tile/wm-gap=20,5pct/src/photos/beach.jpg
```

### Elixir

```elixir
ImagePipe.URL.group(builder,
  watermark: :logo,
  watermark_tile: true,
  watermark_gap: {20, {:pct, 5}}
)
```

<!-- tabs-close -->

## Watermark appearance

- Transparent parts of the watermark leave the image underneath unchanged.
- An image without transparency stays opaque. A transparent image becomes
  opaque where the watermark is opaque.
- The watermark is turned upright according to its own EXIF orientation.
- An animated or multi-page watermark file draws a single still frame.
- A color watermark on a grayscale image turns the result into a color image.
- Partly transparent parts of the watermark blend in sRGB when
  [`profile`](output.md#profile) is `strip` or `srgb`. With `preserve`,
  `display-p3`, or `adobe-rgb`, they blend in the original's color profile, so
  the result can differ slightly over colors that sRGB can't show.
- The response's metadata and color profile come from the main image, never
  from the watermark.

## Watermark errors

These fail with `400` before any image is fetched:

- An unknown `wm` name, or `wm-src64` or `wm-enc` when request-named watermarks
  aren't allowed.
- Two of `wm`, `wm-src64`, and `wm-enc` in one group.

A watermark image that can't be fetched fails the whole request with the same
status as a main image would, such as `404` when it doesn't exist or `413` when
it is too large. A watermark file that isn't a supported image answers `415`.
[Error responses](../errors.md) lists the statuses.