# `OpenFresco`
[🔗](https://github.com/alexdont/open_fresco/blob/v0.2.0/lib/open_fresco.ex#L1)

OG-image / social-card scenes rendered server-side, built to run on
[Fresco](https://hex.pm/packages/fresco) canvases.

An **OG template is a design with holes** — the post title, hero image,
and CTA label are substituted per post (and per locale) at render time,
not baked into the design. `OpenFresco` models that as a `Scene` of
positioned elements whose text/image values may be *placeholders*, then
resolves those values and emits the result.

It produces **SVG** (`render_svg/3`) — the shared intermediate
representation — and **PNG** (`render/3`) via the optional `:resvg`
native dependency (or a CLI rasterizer on PATH). Because the editor
preview and the final raster come from one SVG generator, "what you edit
is what renders" holds by construction.

## Quick start

    scene =
      OpenFresco.Scene.new(width: 1200, height: 630, background: OpenFresco.Scene.solid("#0b1220"))
      |> OpenFresco.Scene.add(
        OpenFresco.Scene.text("title",
          box: %{x: 64, y: 420, w: 1072, h: 150},
          value: OpenFresco.Scene.placeholder("title"),
          size: 68, weight: 700, fill: OpenFresco.Scene.solid("#ffffff")
        )
      )

    svg = OpenFresco.render_svg(scene, %{"title" => "Hello, world"})

## Determinism & caching

`render_svg/3` is a pure function of `(scene, values)`. `version/0`
returns the generator version string — fold it into cache keys so cached
output invalidates when the generator changes.

## Not fresco-coupled at render time

A `Scene` is standalone. It is *carried inside* a Fresco canvas's
`extensions["open_fresco"]` blob for editing and storage (non-breaking
for etcher/tessera, which own their own extension keys), but rendering
needs no fresco dependency — this package depends only on `:jason`.

# `rasterizer_available?`

```elixir
@spec rasterizer_available?() :: boolean()
```

Whether a PNG rasterizer backend is available (see `render/3`).

# `render`

```elixir
@spec render(OpenFresco.Scene.t(), map(), keyword()) ::
  {:ok, binary(), %{width: pos_integer(), height: pos_integer()}}
  | {:error, term()}
```

Render a scene to **PNG bytes** — `{:ok, png, %{width, height}}` or
`{:error, term}`. Browser-free, deterministic, never raises. Requires a
rasterizer backend (the optional `:resvg` dep, or a CLI on PATH); returns
`{:error, :rasterizer_missing}` when none is reachable. See
`OpenFresco.Renderer`.

# `render_svg`

```elixir
@spec render_svg(OpenFresco.Scene.t(), map(), keyword()) :: binary()
```

Render a scene to SVG (a binary), resolving `values`.

`values` maps `{{slot}}` (and `[[global]]`) names to resolved values.
Inline tokens are substituted inside text and image strings; an unwired
`{{slot}}` passes through visibly (og's "needs wiring" signal), and a
missing image slot draws the neutral "unresolved image" stand-in. Locale
is a render input — pass locale-resolved values (or a `[[page_locale]]`
global).

Options:
  * `:globals` — a map of `[[global]]` values (site host, page URL,
    locale…), merged with `values` before substitution.
  * `:max_dimension` — clamp canvas width/height to this many pixels
    (default `10_000`) as an OOM guard against crafted scenes.
  * `:remote_images` — external-resource policy. By default remote
    (`http(s)://`, protocol-relative) and `file:` image hrefs are
    **denied** and draw the unresolved stand-in: scenes are potentially
    attacker-influenced DB content, and browsers/CLI rasterizers would
    otherwise fetch at render time (SSRF) or read local files. Pass
    `:allow` to permit `http(s)` for hosts that accept that surface.
    `data:` URLs always pass.
  * `:id_prefix` — namespace for generated SVG defs ids (gradients,
    masks, clips, filters). Defaults to a scene-derived hash; pass a
    per-instance id when inlining multiple *different* renders into one
    page so their `url(#id)` references can't cross-wire (the editor
    component passes its component id automatically).

# `version`

```elixir
@spec version() :: String.t()
```

The SVG-generator version string, for folding into cache keys.

---

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