Use Your Brand Fonts in Programmatically Generated Images

6 min read Image Generation

System Fonts Are Not Your Brand

Your brand uses a custom typeface. Maybe it’s a licensed font like Circular, Graphik, or GT Walsheim. Maybe it’s a Google Font that’s become part of your visual identity. Either way, it’s not Arial or Helvetica, and rendering your generated images in a system font defeats the purpose of programmatic image generation.

Most image generation approaches make custom fonts painful. Canvas-based tools need the font installed on the server — which means baking it into the Docker image or mounting it as a volume. Puppeteer needs the font installed in the OS, registered with fontconfig, and available before the page renders. Serverless environments make it even harder — you’re fighting with Lambda layers or pre-built binaries to get a font file onto the filesystem.

The Image Generation API takes a different approach. Fonts are part of the request payload. Upload your font files — TTF, OTF, WOFF, or WOFF2 — in the fonts array, reference them by name in your text layers, and the API renders with your exact typeface. No server configuration. No Docker layers. No fontconfig.

Uploading Fonts

The fonts array at the top level of the request defines all fonts available to text layers. Each entry maps a combination of font name, weight, and style to a font file.

import { IterationLayer } from "iterationlayer";

const client = new IterationLayer({ apiKey: "YOUR_API_KEY" });

const result = await client.generateImage({
  dimensions: { width: 1200, height: 630 },
  output_format: "png",
  fonts: [
    {
      name: "Circular",
      weight: "Regular",
      style: "normal",
      file: { type: "url", name: "CircularStd-Book.woff2", url: "https://example.com/fonts/CircularStd-Book.woff2" },
    },
    {
      name: "Circular",
      weight: "Bold",
      style: "normal",
      file: { type: "url", name: "CircularStd-Bold.woff2", url: "https://example.com/fonts/CircularStd-Bold.woff2" },
    },
  ],
  layers: [
    {
      index: 0,
      type: "solid-color-background",
      hex_color: "#0f172a",
    },
    {
      index: 1,
      type: "text",
      text: "Your Brand. Your Font.",
      font_name: "Circular",
      font_weight: "Bold",
      font_size_in_px: 56,
      text_color: "#ffffff",
      text_align: "center",
      vertical_align: "center",
      position: { x: 60, y: 200 },
      dimensions: { width: 1080, height: 230 },
    },
  ],
});

The font_name on the text layer matches the name in the fonts array. The font_weight on the text layer matches the weight in the fonts array (e.g., "Bold" matches "Bold"). The API resolves the font file from these matches.

Font Files: The Basics

Each font variant — Regular, Bold, Italic, Bold Italic, Light, Medium, etc. — is a separate file. This isn’t an API limitation. It’s how fonts work. Inter Regular (Inter-Regular.woff2) and Inter Bold (Inter-Bold.woff2) are different files with different glyph outlines.

The fonts array reflects this reality. Each entry is one variant:

const fonts = [
  {
    name: "Inter",
    weight: "Regular",
    style: "normal",
    file: { type: "url", name: "Inter-Regular.woff2", url: "https://example.com/fonts/Inter-Regular.woff2" },
  },
  {
    name: "Inter",
    weight: "Bold",
    style: "normal",
    file: { type: "url", name: "Inter-Bold.woff2", url: "https://example.com/fonts/Inter-Bold.woff2" },
  },
  {
    name: "Inter",
    weight: "Regular",
    style: "italic",
    file: { type: "url", name: "Inter-Italic.woff2", url: "https://example.com/fonts/Inter-Italic.woff2" },
  },
  {
    name: "Inter",
    weight: "Bold",
    style: "italic",
    file: { type: "url", name: "Inter-BoldItalic.woff2", url: "https://example.com/fonts/Inter-BoldItalic.woff2" },
  },
];

Four entries for four variants. In practice, most templates need two — Regular and Bold. Add Italic if you use markdown italic in your text layers.

Font Weight and Style Mapping

The weight property uses string names:

Weight Description
“Thin” Thinnest available weight
“ExtraLight” Extra light weight
“Light” Light weight
“Regular” Standard weight (also called Book)
“Medium” Medium weight
“SemiBold” Semi bold weight
“Bold” Bold weight
“ExtraBold” Extra bold weight
“Black” Heaviest available weight

The style property is either "normal" or "italic".

When a text layer specifies font_weight: "Bold", the API looks for a font entry with name matching font_name and weight: "Bold". When markdown **bold** is used, the API looks for weight “Bold” regardless of the layer’s font_weight. When markdown *italic* is used, it looks for style: "italic" with the same weight.

File Formats

The API accepts four font formats:

  • WOFF2 — smallest file size, modern compression. Use this when possible.
  • WOFF — slightly larger than WOFF2, wide support.
  • TTF (TrueType) — uncompressed, larger files, universal compatibility.
  • OTF (OpenType) — similar to TTF, supports more advanced typographic features.

WOFF2 is the best choice for API usage. Smaller files mean faster uploads, which means faster image generation. If your font vendor provides WOFF2 files, use them.

Hosting Font Files

The file field accepts a URL. Host your font files wherever makes sense — your CDN, an S3 bucket, your own server. The API fetches the file at generation time.

// From a CDN
{ type: "url", name: "BrandFont-Regular.woff2", url: "https://cdn.example.com/fonts/BrandFont-Regular.woff2" }

// From S3
{ type: "url", name: "BrandFont-Regular.woff2", url: "https://s3.amazonaws.com/your-bucket/fonts/BrandFont-Regular.woff2" }

For fonts you use in every generated image, host them on a CDN with long cache headers. The font file doesn’t change between requests — there’s no reason to serve it from a slow origin each time.

You can also pass font files as base64-encoded data:

{ type: "base64", name: "BrandFont-Regular.woff2", base64: "d09GMgABAAAAA..." }

Base64 is useful for testing or when you can’t host files at a URL. For production use, URLs are more practical — they keep your request payload smaller and let the API cache the font data.

A Complete Brand Template

Here’s a branded event announcement card using a custom typeface with three weights:

const generateEventCard = async (eventName: string, date: string, venue: string) => {
  const { data: { buffer } } = await client.generateImage({
    dimensions: { width: 1080, height: 1080 },
    output_format: "png",
    fonts: [
      {
        name: "Sora",
        weight: "Light",
        style: "normal",
        file: { type: "url", name: "Sora-Light.woff2", url: "https://cdn.example.com/fonts/Sora-Light.woff2" },
      },
      {
        name: "Sora",
        weight: "Regular",
        style: "normal",
        file: { type: "url", name: "Sora-Regular.woff2", url: "https://cdn.example.com/fonts/Sora-Regular.woff2" },
      },
      {
        name: "Sora",
        weight: "Bold",
        style: "normal",
        file: { type: "url", name: "Sora-Bold.woff2", url: "https://cdn.example.com/fonts/Sora-Bold.woff2" },
      },
    ],
    layers: [
      {
        index: 0,
        type: "solid-color-background",
        hex_color: "#0a0a0a",
      },
      {
        index: 1,
        type: "rectangle",
        hex_color: "#6366f1",
        position: { x: 0, y: 0 },
        dimensions: { width: 1080, height: 6 },
      },
      {
        index: 2,
        type: "text",
        text: eventName,
        font_name: "Sora",
        font_weight: "Bold",
        font_size_in_px: 52,
        text_color: "#f8fafc",
        text_align: "left",
        vertical_align: "center",
        is_splitting_lines: true,
        position: { x: 80, y: 280 },
        dimensions: { width: 920, height: 250 },
      },
      {
        index: 3,
        type: "text",
        text: date,
        font_name: "Sora",
        font_weight: "Regular",
        font_size_in_px: 28,
        text_color: "#6366f1",
        text_align: "left",
        vertical_align: "top",
        position: { x: 80, y: 570 },
        dimensions: { width: 920, height: 40 },
      },
      {
        index: 4,
        type: "text",
        text: venue,
        font_name: "Sora",
        font_weight: "Light",
        font_size_in_px: 22,
        text_color: "#a1a1aa",
        text_align: "left",
        vertical_align: "top",
        position: { x: 80, y: 630 },
        dimensions: { width: 920, height: 35 },
      },
      {
        index: 5,
        type: "static-image",
        file: { type: "url", name: "logo-white.png", url: "https://cdn.example.com/brand/logo-white.png" },
        position: { x: 80, y: 920 },
        dimensions: { width: 160, height: 50 },
      },
    ],
  });

  return Buffer.from(buffer, "base64");
};

Three font weights — Light, Regular, and Bold — all from the same typeface. The event name is bold for impact. The date is regular weight in the brand color. The venue is light weight in muted gray. The visual hierarchy comes from weight variation within a single font family, exactly as a designer would do it.

Mixing Font Families

Nothing limits you to one font family. Some brand systems use a display font for headlines and a text font for body copy. Define both in the fonts array and reference each by name:

const fonts = [
  {
    name: "Playfair Display",
    weight: "Bold",
    style: "normal",
    file: { type: "url", name: "PlayfairDisplay-Bold.woff2", url: "https://cdn.example.com/fonts/PlayfairDisplay-Bold.woff2" },
  },
  {
    name: "Source Sans",
    weight: "Regular",
    style: "normal",
    file: { type: "url", name: "SourceSans3-Regular.woff2", url: "https://cdn.example.com/fonts/SourceSans3-Regular.woff2" },
  },
];

// Headline layer
{
  index: 1,
  type: "text",
  font_name: "Playfair Display",
  font_weight: "Bold",
  // ...
}

// Body layer
{
  index: 2,
  type: "text",
  font_name: "Source Sans",
  font_weight: "Regular",
  // ...
}

Serif headline, sans-serif body. A classic combination that’s trivial to set up.

Font Licensing

A note on licensing. Uploading a font file to the API means sending that file over HTTPS with each request. Check your font license to make sure this is permitted. Most commercial font licenses cover server-side rendering. Some don’t. Google Fonts and other open-source fonts have no restrictions. When in doubt, check the license terms or use an open-source alternative.

What’s Next

Generated images work with the same auth and credit pool as Document Extraction and Image Transformation — chain them in a single pipeline.

Get Started

Check out the Image Generation API docs for the full font specification. Upload your brand fonts, reference them in text layers, and start generating on-brand images programmatically. What used to require server-side font installation and OS-level configuration is now a JSON array in your API request.

Sign up for a free account — no credit card required.

Start building in minutes

Free trial included. No credit card required.