# `Hakushi.Images`
[🔗](https://github.com/hattoriai/hakushi/blob/v0.6.0/lib/hakushi/images.ex#L2)

The images of a canvas (`Hakushi.Kits.Images`), checked on the server
before they become nodes, and kept in the app's store with
[Storehouse](https://hexdocs.pm/storehouse).

The browser's content type is a claim. Storehouse reads the bytes of
the file (`Storehouse.Check`): a PNG, JPEG, GIF or WebP image, from its
signature, with its width and height from its header (a JPEG's turned
as its EXIF orientation says, as browsers draw it). Any other file is
refused, an SVG too (it may hold a script), and an image whose header
has no size.

`store/2` checks a file and stores it under a new key, in the store of
`config :storehouse, adapter: ...` (the local disk, S3, or one of the
app's); `fetch/2` downloads an image from an address of the hosts an
app allows, then stores it, so an image pasted by its address, or added
by an agent, is the app's own file as an upload is.

# `image`

```elixir
@type image() :: Storehouse.File.t()
```

A stored image: a `Storehouse.File`, with its `key` and `url` in the
store, the client's `name`, its checked `content_type`, `bytes`,
`width`, `height` and `checksum`, and the URL of a smaller copy
(`preview`), or `nil`.

# `content_type`

```elixir
@spec content_type(String.t()) :: String.t() | nil
```

The content type of an image's file extension (`".png"`), or `nil` for
another.

# `extensions`

```elixir
@spec extensions() :: [String.t()]
```

The file extensions of the images a canvas takes, as an upload's `accept`.

# `fetch`

```elixir
@spec fetch(String.t(), keyword()) :: {:ok, image()} | {:error, term()}
```

Downloads the image at `url`, from a host of `:hosts`, then checks and
stores it as `store/2` does (`Storehouse.fetch/2`).

Only `https` addresses (and `http`, with `http: true`) of the hosts the
app allows are fetched, and a redirect only to one of them: the server
never fetches an address a person or an agent chose freely, which could
be one of the app's own network. A host is a name (`"images.example.com"`)
or a name with a leading `*.` for its subdomains (`"*.example.com"`).

## Options

  * `:hosts` - the hosts it fetches from (required).
  * `:http` - whether `http` addresses are fetched. The default is
    `false`.
  * `:timeout` - in milliseconds, for the whole download. The default
    is 10 seconds.
  * the options of `store/2`.

# `fetchable?`

```elixir
@spec fetchable?(String.t(), [String.t()]) :: boolean()
```

Whether `url` is an address that `fetch/2` takes with `hosts` (`https`
only).

    iex> Hakushi.Images.fetchable?("https://images.example.com/a.png", ["*.example.com"])
    true

    iex> Hakushi.Images.fetchable?("https://example.org/a.png", ["*.example.com"])
    false

# `safe_url?`

```elixir
@spec safe_url?(term()) :: boolean()
```

Whether `url` is an address an image node may show: `http` or `https`,
or a path of the app (`/uploads/…`).

# `store`

```elixir
@spec store(Path.t(), keyword()) :: {:ok, image()} | {:error, term()}
```

Checks the image at `path` and stores it under a new key, with
`Storehouse.store/2`. The file at `path` is left where it is.

A file that is not an image a canvas takes gives
`{:error, :not_an_image}`; one over a limit, `{:error, :too_large}`.

## Options

  * `:name` - the file's name, for its key and its node. The default is
    the base name of `path`.
  * `:adapter` - the store. The default is `Storehouse.adapter/0`.
  * `:thumbnail` - a function of the stored image (a `Storehouse.File`)
    and `path` that gives the URL of a smaller copy, or `nil`: the app
    makes and stores it (with `image` or `vix`, say). The canvas shows it
    when it is zoomed out. An exception, or a result that is not a URL,
    gives none.
  * `:max_bytes` - the largest file. The default is 10 MB.
  * `:max_pixels` - the most pixels (width × height). The default is
    50 million.

# `types`

```elixir
@spec types() :: [String.t()]
```

The content types of the images a canvas takes.

---

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