README.md

# OGI

Generates and serves OpenGraph Images using Typst.

Inspired by [OG-Image](https://github.com/svycal/og-image/tree/main) but uses Typst instead of Chrome+Puppeteer, so you can add it directly to your Phoenix app.

## Installation

```elixir
def deps do
  [
    {:ogi, "~> 0.1.0"}
  ]
end
```

## Setup

You need these three things:

1. A Typst template.
    - LLMs are pretty good at generating those and you can test them quickly on [typst.app/play](https://typst.app/play/)
2. A Phoenix Controller and Route.
3. An `og:image` metatag in your `<head>` tag.

Below is an example controller for serving OG Images for a blog post.

```elixir
defmodule BlogWeb.ImageController do
  use BlogWeb, :controller

  alias Blog.Posts

  def show(conn, %{"id" => blog_id}) do
    post = Posts.get_post_by_id!(blog_id)
    assigns = [title: post.title]
    Ogi.render_image(conn, "#{blog_id}.png", typst_markup(), assigns)
  end

  defp typst_markup do
    # Your generated Typst markup goes here.
    #
    # You can dynamically inline variables with:
    # Blog Title: <%= title %>
    #
    # Note: There is *no* @ before the variable other than in HEEx templates!
  end
end
```

Then add this route to your router:

```elixir
scope "/", BlogWeb do
  get "/og-image/:id", ImageController, :show
end
```

For adding dynamic Metatags, I recommend the [Metatags](https://github.com/johantell/metatags) library:

```elixir
# In your Controller or LiveView serving the blog post, add this:
def handle_params(%{"id" => post_id}, _url, socket) do
  post = Posts.get_post_by_id!(post_id)

  socket =
    socket
    |> Metatags.put("og:title", post.title)
    |> Metatags.put("og:description", post.description)
    |> Metatags.put("og:image", url(~p"/og-image/#{post_id}"))

  {:ok, socket}
end
```

And that's it! You can test this by navigating to the route manually or by using a browser extension that previews OpenGraph information for a website.

## Configuration

Currently, OGI only supports the following configuration:

```elixir
# Whether to cache rendered images or not (default: true)
config :ogi, cache: true|false
```

## Caveats

### Missing fonts

You need to use a font that is available on your system, otherwise Typst will fall back to a `serif` font.

For local development, you can run `typst fonts` to list all available fonts.

For remote deployment, I recommend researching which fonts are installed by default on your runner OS, for example on [Debian](https://wiki.debian.org/Fonts). You can always install the font you like with the package manager and it will become automatically available to Typst.

## ToDo's

- [ ] Clean up Cache when a certain size is reached
- [ ] Add fallback OG Image option if render fails
- [ ] Support for templates