# `ImagePipe`
[🔗](https://github.com/hlindset/image_pipe/blob/v0.2.0/image_pipe/lib/image_pipe.ex#L1)

Processes images in your own code, and holds the configuration that
`ImagePipe.Plug` serves images with.

    builder =
      ImagePipe.URL.new()
      |> ImagePipe.URL.group(resize: [width: 400, height: 300, fit: :cover])
      |> ImagePipe.URL.output(format: :webp)

    {:ok, result} = ImagePipe.run(ImagePipe.config(), builder, {:file, "cat.jpg"})

`config/1` builds a configuration, `ImagePipe.URL` describes what to do to
the image, and `run/4` returns the result. `write/5` writes it to a file
instead. [Processing images in Elixir](processing-in-elixir.md) walks
through it.

An instance is a supervised process that holds a configuration and starts
the processes its caches need. Run one with `child_spec/1` when a mount's
configuration reads runtime values, such as environment variables, or when
the configuration uses a cache. `ImagePipe.Plug` mounts, `run/4`,
`write/5`, `validate/2`, and `url_config/2` find it by name.

# `child_spec`

```elixir
@spec child_spec(keyword()) :: Supervisor.child_spec()
```

Returns a child specification that runs a named ImagePipe instance.

Start the instance in your application's supervision tree, before the
endpoint that mounts it. Its options are evaluated when the application
starts, so they can read environment variables:

    children = [
      {ImagePipe,
       name: MyApp.Images,
       sources: [...],
       cache: [root: "/var/cache/image_pipe/processed", max_size_bytes: 5_000_000_000]},
      MyAppWeb.Endpoint
    ]

Mount it with the `:instance` option of `ImagePipe.Plug`, and pass its name
to `run/4`, `write/5`, `validate/2`, and `url_config/2`.

A configuration with `cache` or `input_cache` must be used through an
instance, which starts the processes that track each cache's size.
An `ImagePipe.Plug` mount without `:instance`, and `run/4` with a
configuration from `config/1`, raise `ArgumentError` for a configuration
with a cache.
[Caching processed images](caching-processed-images.md) covers setting up a
cache.

## Options

* `:name` (`t:atom/0`) - Required. Name that `ImagePipe.Plug` mounts, `ImagePipe.run/4`, `ImagePipe.write/5`, `ImagePipe.validate/2`, and `ImagePipe.url_config/2` use to find the instance. It also names the instance's supervisor.

* `:mounts` (`t:keyword/0`) - Named sets of URL options, such as `[signed: [keys: [key]]]`. An `ImagePipe.Plug` mount picks one with its `:mount` option, so mounts with different signing keys share one instance and its caches. Each set takes the URL options of `ImagePipe.config/1` (`:base_url`, `:keys`, `:encrypt_source`, `:source_encryption_keys`, `:iv_mode`) and applies them on top of the instance's own, so an option a set leaves out keeps the instance's value. For a mount that doesn't check signatures, set `keys: []`, and `source_encryption_keys: []` if the instance has them. The default value is `[]`.

* `:config` (`t:ImagePipe.Config.t/0`) - A configuration from `ImagePipe.config/1` to start from. The other options override it.

* `:detector_warmup` (`:all`, `false`, or a list of class names) - Which detector classes to load models for when the instance starts, using `ImagePipe.Transform.Detector.Warmup`. `:all` loads every model, a list such as `["face"]` loads only the models those classes need, and `false` loads none. Nothing is loaded when the configuration's detector isn't available. A class the detector doesn't support raises `ArgumentError`. The default value is `:all`.

Every option of `config/1` is accepted too. Invalid options raise
`ArgumentError` when the child specification is built, before the
instance starts.

# `config`

```elixir
@spec config(keyword()) :: ImagePipe.Config.t()
```

Builds the configuration that `ImagePipe.Plug` mounts, instances, and
`run/4` use.

    config =
      ImagePipe.config(
        sources: [
          media: [
            adapter: ImagePipe.Source.File,
            match: :path,
            options: [root: "/srv/images", root_id: "media"]
          ]
        ],
        presets: %{"card" => "w=400/h=300/fit=cover"},
        quality: 82
      )

Invalid options raise `ArgumentError`, without credentials in the message.
Building a configuration reads no sources or caches. Inspecting one hides its
values.

[Elixir configuration](configuration.md) shows which settings belong here and
which belong to a mount, a source, or a request.

`keys` and `source_encryption_keys` set which URLs an `ImagePipe.Plug`
mount accepts. `base_url`, `encrypt_source`, and `iv_mode` only affect URLs
built from the configuration with `url_config/2`.

## Options

* `:base_url` (`t:String.t/0`) - Prefix for generated URLs: an HTTP(S) URL such as
  `"https://cdn.example.com/images"`, a root-relative path such as
  `"/images"`, or a relative path such as `"images"`. A trailing `/` is
  removed. Path segments use unescaped ASCII letters, digits, `-`, `.`,
  `_`, or `~`. A base URL with credentials, a query string, a fragment,
  an empty segment, or a `.` or `..` segment raises. With the default,
  URLs start with `/`, such as `/w=400/src/cat.jpg`. The default value is `""`.

* `:keys` (list of hex-encoded `t:String.t/0`) - Signing keys, each at least 64 hex digits (32 bytes), such as
  the output of `openssl rand -hex 32`. A shorter key raises
  `ArgumentError`. The first key signs generated URLs. The server
  accepts a signature from any key in the list, so keep an old
  key while URLs signed with it are in use. Without keys, URLs
  are unsigned and the server rejects signed ones. The default value is `[]`.

* `:encrypt_source` (`t:boolean/0`) - Encrypts the source, and any watermark source, in generated URLs,
  which then use `enc/<token>` instead of `src/<path>`. Requires
  `:source_encryption_keys`. A server with encryption keys
  decrypts `enc/` sources regardless of this option. The default value is `false`.

* `:source_encryption_keys` (list of hex-encoded `t:String.t/0`) - Keys that encrypt and decrypt sources, each 32 bytes written as
  64 hex digits in either case, such as the output of
  `openssl rand -hex 32`. The first key encrypts. The server
  decrypts with any key in the list. The keys must differ from
  the signing keys, and setting them requires `:keys`. The default value is `[]`.

* `:iv_mode` (`:deterministic` or `:random`) - How `ImagePipe.URL.url/3` picks the initialization vector for an
  encrypted source. `:deterministic` derives it from the source with
  a secret key, so one source always gives the same URL.
  `:random` gives a new URL on every call. The default value is `:deterministic`.

* `:sources` (`t:keyword/0`) - Named sources that originals are read from, as `name: [adapter: module, match: rule, options: [...]]`. See [sources](sources.md#routing-image-paths-to-sources).

* `:source_cache_policy` (`t:keyword/0`) - Default cache storage and freshness policy for every source. See `ImagePipe.Source.CachePolicy` and [source cache settings](cache.md#source-cache-settings).

* `:max_body_bytes` (`t:pos_integer/0`) - Maximum size of an original, in bytes. A larger original fails the request. The default value is `10000000`.

* `:max_input_pixels` (`t:pos_integer/0`) - Maximum pixels of a decoded original. For an animation, counts the frames composited to reach the requested `page`. A larger original fails the request. The default value is `40000000`.

* `:max_input_frames` (`t:pos_integer/0`) - Maximum frames or pages an original may declare. An original with more fails the request. The default value is `1000`.

* `:svg` (`t:keyword/0`) - How SVG originals render. Rendering needs the optional `image_pipe_svg` package; without it, SVG originals fail with `415` and setting this option raises. The default value is `[]`.

  * `:font_dirs` (list of `t:String.t/0`) - Directories searched, with their subdirectories, for the TrueType and OpenType fonts (`.ttf`, `.otf`, `.ttc`, `.otc`) that SVG text renders in. System fonts are never used, so without fonts SVG text isn't drawn. A directory that doesn't exist, or a file that isn't a valid font, raises. The default value is `[]`.

  * `:fallback_family` (`t:String.t/0`) - Font family that text renders in when the SVG names a family that isn't loaded, or a generic one such as `sans-serif`. Must be one of the fonts in `:font_dirs`. Without it, that text isn't drawn.

  * `:max_bytes` (`t:pos_integer/0`) - Maximum size of an SVG original, in bytes. A larger one fails the request. The default value is `10000000`.

  * `:max_render_work` (`t:pos_integer/0`) - Maximum estimated work to render an SVG: the pixels its layers, masks, filters, and images process, weighted by how slow each is. The default allows about a second of rendering. A heavier SVG fails the request before it renders. The default value is `100000000`.

  * `:max_render_pixels` (`t:pos_integer/0`) - Maximum pixels, at 4 bytes each, of an SVG's images held at once while it renders: the canvas, plus a layer for each group with opacity, a filter, a clip path, or a mask. Defaults to four times `:max_input_pixels`, a canvas of the largest size allowed and three layers, which with the default `:max_input_pixels` is about 640 MB. An SVG that needs more fails the request before it renders.

* `:max_result_width` (`t:pos_integer/0`) - Maximum output width. Larger results are scaled down to fit. The default value is `8192`.

* `:max_intermediate_pixels` (`t:pos_integer/0`) - Maximum pixels in a decoded frame buffered, downsampled, or delivered during processing. Larger frames fail with `422`. Lazy intermediate frames may be larger when a crop reduces them before those steps. The default value is `100000000`.

* `:max_result_height` (`t:pos_integer/0`) - Maximum output height. Larger results are scaled down to fit. The default value is `8192`.

* `:max_result_pixels` (`t:pos_integer/0`) - Maximum output pixels. Larger results are scaled down to fit. The default value is `40000000`.

* `:processing_pool` (`t:atom/0` or `t:pid/0`) - A running `ImagePipe.ProcessingPool`, by name or PID, that limits how many images are processed at once. See [limiting concurrent processing](processing-controls.md).

* `:auto_avif` (`t:boolean/0`) - Serve AVIF when the request's `Accept` header lists it. The default value is `true`.

* `:auto_webp` (`t:boolean/0`) - Serve WebP when the request's `Accept` header lists it. The default value is `true`.

* `:format_order` (list of `:avif` and `:webp`) - Which format wins when `Accept` lists both. A format left out of the list comes after the listed ones. The default value is `[:avif, :webp]`.

* `:quality` (`t:pos_integer/0`) - Encoder quality, `1..100`, for formats without a `:format_quality`. The default value is `80`.

* `:format_quality` (map of `t:atom/0` to `t:pos_integer/0`) - Quality per output format, `1..100`. Each format it sets replaces that format's default. A request's `q` replaces this table, and its `format-q` replaces the entries for the formats it lists. See [`format-q`](processing/output.md#format-q). The default value is `%{avif: 63, webp: 79}`.

* `:strip_metadata` (`t:boolean/0`) - Remove EXIF, XMP, and other optional metadata from the output. The default value is `true`.

* `:keep_copyright` (`t:boolean/0`) - Keep copyright and artist fields when stripping metadata. The default value is `true`.

* `:stripped_dpi` (`t:pos_integer/0`) - Density written to the output, `1..65535`, when metadata is stripped and the request has no `dpi`. The default value is `72`.

* `:strip_color_profile` (`t:boolean/0`) - Convert the output to sRGB (or gray) and leave out the original's ICC profile. `false` keeps the original's profile. The default value is `true`.

* `:preserve_hdr` (`t:boolean/0`) - Keep high bit depth in output formats that support it. The default value is `false`.

* `:skip_processing_formats` (list of `t:atom/0`) - Original formats, such as `[:gif]`, served unchanged instead of processed when the request names no other `format` and draws no watermark. The unchanged original keeps its metadata, including any location data, and only `:max_body_bytes` limits it. See [formats](processing/output.md#format). The default value is `[]`.

* `:autoquality` (`t:boolean/0`) - Picks each image's quality to meet `:autoquality_target` by encoding and scoring several qualities. A request can turn it on or off and set its own target with [`autoquality`](processing/output.md#autoquality). The default value is `false`.

* `:autoquality_target` (`t:number/0`) - The SSIMULACRA2 score auto-quality aims for, above `0` and up to `100`. The default value is `75`.

* `:jpeg_options` (`t:keyword/0`) - Default JPEG encoder settings, as a keyword list with the fields of the `jpeg_options:` option of `ImagePipe.URL.output/2`, such as `[interlace: true]`. A request's settings win field by field. See [encoder options](processing/output.md#encoder-options) and the builder names in [URL option names](`ImagePipe.URL#module-url-option-names`).

* `:png_options` (`t:keyword/0`) - Default PNG encoder settings, as for `:jpeg_options`.

* `:webp_options` (`t:keyword/0`) - Default WebP encoder settings, as for `:jpeg_options`. `:effort` defaults to `4`.

* `:avif_options` (`t:keyword/0`) - Default AVIF encoder settings, as for `:jpeg_options`. `:effort` defaults to `3` and `:subsample_mode` to `:off`.

* `:detector` (`:default`, `nil`, or `t:module/0`) - The detector for face and object detection. `:default` uses the built-in detector when its dependencies are installed, `nil` turns detection off, and a module uses a custom detector. See [enabling detection](enabling-detection.md). The default value is `:default`.

* `:detector_required` (`t:boolean/0`) - Fail requests that ask for detection when it can't run: `501` when the detector can't detect the requested classes in this build, `503` when its models aren't downloaded, and `500` when detection fails. With `false`, the crop falls back to attention cropping. `anchor=smart-face` always falls back. With `true`, `ImagePipe.config/1` raises `ArgumentError` when the detector can't detect any class. The default value is `false`.

* `:telemetry_prefix` (list of `t:atom/0`) - Prefix of every telemetry event name. See [telemetry](telemetry.md). The default value is `[:image_pipe]`.

* `:clock` (`(-> integer())`) - Returns the current Unix time in seconds, for checking a URL's `expires`. The system clock by default.

* `:cache` (keyword list) - Cache for processed images, stored on disk, such as `[root: "/var/cache/image_pipe/processed", max_size_bytes: 5_000_000_000]`. Takes the options of `ImagePipe.Cache.FileSystem`, and needs an instance (see `ImagePipe.child_spec/1`). See [caching processed images](caching-processed-images.md). Off by default.

* `:input_cache` (keyword list) - Cache for originals fetched from sources. Takes the same options as `:cache` except `:max_body_bytes`. See [originals cache](cache.md#originals-cache). Off by default.

* `:storage_inputs` (list of `{:header, name}` or `{:cookie, name}`) - Request headers and cookies whose values select separate cached copies, such as `[{:header, "x-tenant"}]`. They aren't sent to the source and don't change the image. The default value is `[]`.

* `:watermarks` (map of `t:atom/0` to `t:keyword/0`) - Named watermark images that requests select with `wm=name`, as `%{logo: [source: "brand/logo.png", opacity: 0.6]}`. Names match `[a-z0-9_-]+`. `:source` is an image path that a configured source serves. `:opacity`, above `0` and at most `1`, defaults to `1` and multiplies the request's `wm-opacity`. See [watermarks](processing/watermark.md). The default value is `%{}`.

* `:request_watermarks` (`t:boolean/0`) - Let requests overlay any image the configured sources serve, with `wm-src64` or `wm-enc`, besides the named `:watermarks`. The default value is `false`.

* `:presets` (map of `t:String.t/0` to fragment or `ImagePipe.URL` builder) - Named sets of options that URLs select with `preset=name`, as URL fragments such as `%{"card" => "w=400/h=300/fit=cover"}` or `ImagePipe.URL` builders. See [defining presets](defining-presets.md). The default value is `%{}`.

* `:request_defaults` (fragment or `ImagePipe.URL` builder) - Options applied to the first group of every request, before presets and the request's own options. One group, with no `preset`.

* `:preset_lookup` (`{module, keyword}`) - Looks up preset names that `:presets` doesn't define, per request. See `ImagePipe.PresetLookup` and [storing presets in a database](storing-presets-in-a-database.md).

* `:max_preset_lookups` (`t:pos_integer/0`) - Most distinct names one request may look up. Only with `:preset_lookup`. The default value is `32`.

# `run`

```elixir
@spec run(
  ImagePipe.Config.t() | atom(),
  ImagePipe.URL.t(),
  {:file | :binary | :source, binary()},
  keyword()
) :: {:ok, ImagePipe.Result.t()} | {:error, term()}
```

Runs a builder's plan on an image and returns the encoded result.

    builder =
      ImagePipe.URL.new()
      |> ImagePipe.URL.group(resize: [width: 400])
      |> ImagePipe.URL.output(format: :webp)

    {:ok, result} = ImagePipe.run(ImagePipe.config(), builder, {:file, "photos/cat.jpg"})
    result.content_type
    #=> "image/webp"

`config` comes from `config/1`, or is the name of a running instance. A
configuration with a cache must be passed by its instance's name.
Presets and request defaults come from `config`, as for a request to an
`ImagePipe.Plug` mount. The builder's own URL configuration isn't used.
Matching source bytes, plans, settings, and `Accept` preferences give the
same result as an HTTP request.

## Inputs

  * `{:source, source}` - a source the configuration's
    [configured sources](sources.md#routing-image-paths-to-sources) resolve, as for an HTTP
    request: a path, an HTTP(S) URL such as
    `"https://assets.example.com/cat.jpg"`, an S3 identifier, or a custom
    scheme. Pass the source without the `src/` marker or the URL escaping
    of the request path. The source's network, redirect, timeout, and
    content-type policies apply. These inputs use the configuration's
    caches, so `run/4` and HTTP requests reuse each other's stored copies
    when the plan, `Accept` preferences, and request inputs match.
  * `{:file, path}` - a local file, by absolute path or relative to the
    current working directory. Symlinks are followed, and the path must
    end at a regular file. The path isn't confined to a directory. Use a
    `{:source, path}` with an `ImagePipe.Source.File` source for that.
  * `{:binary, bytes}` - an encoded image in memory, such as an upload.

`{:file, path}` and `{:binary, bytes}` inputs are never cached. Every input
is subject to `:max_body_bytes`, `:max_input_pixels`, and
`:max_input_frames`.

## Options

Every option of `config/1` overrides the configuration for this call, such
as `max_input_pixels: 50_000_000`. Invalid options raise `ArgumentError`.
An instance name takes the same options, except `:cache` and
`:input_cache`, which raise `ArgumentError`: the caches belong to the
instance.
`run/4` and `write/5` also take:

* `:accept` (`t:String.t/0`) - An HTTP `Accept` value, such as `"image/avif,image/webp"`, for choosing the output format when the plan has no `format`. An empty value keeps the original's format where possible, as for a request without `Accept` (see [output formats](requesting-images.md#output-formats)). `:auto_avif`, `:auto_webp`, and `:format_order` apply as configured. The default value is `""`.

* `:request_inputs` (`t:keyword/0`) - The header and cookie values named by the configuration's `:storage_inputs`, for `{:source, source}` inputs. They select the same stored copy as those values in an HTTP request. Header names are case-insensitive, and cookie names are case-sensitive. A missing value matches a request without it. They aren't sent to the source and don't change the result. The default value is `[]`.

  * `:headers` (list of `{String.t(), String.t()}`) - Header values, such as `[{"x-tenant", "one"}]`. Names must be valid header names, and values can't contain control characters. The default value is `[]`.

  * `:cookies` (map of `t:String.t/0` to `t:String.t/0`) - Cookie values by name, such as `%{"session" => "abc"}`. The default value is `%{}`.

The builder's request controls behave as in a URL. `cachebuster` selects a
separate stored copy. `expires` is compared with the configuration's
`:clock`, and a plan is still valid at its exact `expires` second.
`filename`, `attachment`, and `debug` don't change the result.

Options that have no effect, such as `fit: :cover` without a width or
height, are ignored. The `[:request, :ignored_options]`
[telemetry event](telemetry-events.md#request-ignored_options) reports the
ones the builder sets.

## Return values

Returns `{:ok, %ImagePipe.Result{}}`, or `{:error, reason}`:

  * `{:invalid_request, issues}` - an option is unknown or has an invalid
    value, the builder's options don't combine, or it names an unknown
    preset or watermark. `issues` is a list of
    `ImagePipe.Plan.Spec.Issue`.
  * `:expired` - the plan's `expires` time has passed.
  * `{:invalid_output, reason}` - the output options can't be combined,
    such as `color_profile: {:convert, :srgb}` with `hdr: :preserve`. A
    configured default counts too.
  * `{:unsupported_output_format, format}` - no encoder for the format.
  * `{:detector, reason}` - detection is required and `reason` is
    `:unavailable` or `:not_ready`, or the plan names detection classes
    the detector lacks (`{:unknown_classes, names}`).
  * `{:preset, reason}` - the configuration's preset lookup failed:
    `:too_many_presets` when the plan names more presets to look up than
    `:max_preset_lookups` allows, `:lookup_unavailable`, or
    `:invalid_definition`.
  * `{:invalid_source, reason}` - the input isn't one of the tuples above,
    the source isn't valid UTF-8, or the source or a `watermark_source` in
    the plan can't be parsed.
  * `{:source, reason}` - the source or a watermark source couldn't be
    read, such as `{:source, :enoent}` for a missing file,
    `{:source, :not_found}` for a path no configured source matches, or
    `{:source, :body_too_large}`.
  * `{:input_limit, reason}` - the decoded image exceeds
    `:max_input_pixels` or `:max_input_frames`.
  * `{:page_out_of_range, page, pages}` - the builder's `page` doesn't
    exist in the original.
  * `{:decode, reason}` - the input isn't an image ImagePipe can decode.
  * `{:transform, reason}` - processing failed, such as a `region`
    outside the image.
  * `{:encode, reason}` or `{:encode, exception, stacktrace}` - encoding
    failed.
  * `{:processing, reason}` - the processing pool rejected or stopped the
    work, such as `:overloaded`, `:queue_timeout`, or `:timeout`.

The checks that don't need the image run first, so `{:invalid_request, _}`,
`:expired`, `{:invalid_output, _}`, `{:unsupported_output_format, _}`,
`{:detector, _}`, and `{:preset, _}` return before any source is read. Errors raised by
transform code propagate.

## Resources

The result is complete when `run/4` returns, and holds no open file,
source, or image. Source resources are closed before `run/4` returns, on
success and on failure. The result is held in memory, so running large
outputs concurrently needs memory for each complete result.

# `url_config`

```elixir
@spec url_config(ImagePipe.Config.t() | atom(), keyword()) :: ImagePipe.URL.Config.t()
```

Returns the URL settings a configuration or a running instance serves, for
building URLs with `ImagePipe.URL.new/1`.

    MyApp.Images
    |> ImagePipe.url_config()
    |> ImagePipe.URL.new()
    |> ImagePipe.URL.group(resize: [width: 400])
    |> ImagePipe.URL.url!("images/cat.jpg")

The result carries the configuration's presets, request defaults, and
watermark settings as its `:validate_against` option, so
`ImagePipe.URL.validate/1` and `ImagePipe.URL.url/3` check plans against
them.

## Options

* `:mount` (`t:atom/0`) - One of the instance's `:mounts`, whose URL options the result uses instead of the instance's own.

Raises `ArgumentError` for an invalid option, when no instance with the
name is running or it has no mount with that name, or when `:mount` comes
with a configuration.

# `validate`

```elixir
@spec validate(ImagePipe.Config.t() | atom(), ImagePipe.URL.t()) ::
  {:ok, [ImagePipe.Plan.Spec.Issue.t()]} | {:error, term()}
```

Checks a builder's plan as this configuration would serve it.

Applies request defaults and presets, including the preset lookup, then
checks the request, its output policy, and its watermark sources. Returns
`{:ok, warnings}`, the `ImagePipe.Plan.Spec.Issue` warnings for options
`run/4` would ignore and for mistakes the builder repaired, or the error
`run/4` would return, without reading a source or accessing a cache.

# `write`

```elixir
@spec write(
  ImagePipe.Config.t() | atom(),
  ImagePipe.URL.t(),
  {:file | :binary | :source, binary()},
  Path.t(),
  keyword()
) :: {:ok, ImagePipe.Result.t()} | {:error, term()}
```

Runs a builder's plan like `run/4`, then writes the result to `path`.

    {:ok, result} =
      ImagePipe.write(ImagePipe.config(), builder, {:binary, upload}, "thumbs/cat.webp")

Takes the same inputs and options, and returns the same result, as
`run/4`. The file holds `result.data`, with an `:info` result written as
JSON. An existing file is overwritten.

The output format comes from the plan or from `:accept`, never from the
file name, so `"cat.jpg"` can hold WebP. Set `format:` with
`ImagePipe.URL.output/2` to match the file name.

Returns `run/4`'s errors, or `{:error, {:destination, reason}}` when the
file can't be written, such as `{:destination, :enoent}` for a missing
directory. The file is written after source resources are closed.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
