satori README

The project's README from github.com/vercel/satori. As one markdown file: README.md.

Satori

Satori: Enlightened library to convert HTML and CSS to SVG.

Note

To use Satori in your project to generate PNG images like Open Graph images and social cards, check out our announcement and Vercel’s Open Graph Image Generation docs →

To use it in Next.js, take a look at the Open Graph Image Generation examples →

Overview

Satori supports the JSX syntax, which makes it very straightforward to use. Here’s an overview of the basic usage:

// api.jsx
import satori from 'satori'

const svg = await satori(
  <div style={{ color: 'black' }}>hello, world</div>,
  {
    width: 600,
    height: 400,
    fonts: [
      {
        name: 'Roboto',
        // Use `fs` (Node.js only) or `fetch` to read the font as Buffer/ArrayBuffer and provide `data` here.
        data: robotoArrayBuffer,
        weight: 400,
        style: 'normal',
      },
    ],
  },
)

Satori will render the element into a 600×400 SVG, and return the SVG string:

'<svg ...><path d="..." fill="black"></path></svg>'

Under the hood, it handles layout calculation, font, typography and more, to generate a SVG that matches the exact same HTML and CSS in a browser.


Documentation

JSX

Satori only accepts JSX elements that are pure and stateless. You can use a subset of HTML elements (see section below), or custom React components, but React APIs such as useState, useEffect, dangerouslySetInnerHTML are not supported.

Experimental: builtin JSX support

Satori has an experimental JSX runtime that you can use without having to install React. You can enable it on a per-file basis with @jsxImportSource pragmas. In the future, it will autocomplete only the subset of HTML elements and CSS properties that Satori supports for better type-safety.

/** @jsxRuntime automatic */
/** @jsxImportSource satori/jsx */

import satori from 'satori';
import { FC, JSXNode } from 'satori/jsx';

const MyComponent: FC<{ children: JSXNode }> = ({ children }) => (
  <div style={{ color: 'black' }}>{children}</div>
)

const svg = await satori(
  <MyComponent>hello, world</MyComponent>,
  options,
)

Use without JSX

If you don't have JSX transpiler enabled, you can simply pass React-elements-like objects that have type, props.children and props.style (and other properties too) directly:

await satori(
  {
    type: 'div',
    props: {
      children: 'hello, world',
      style: { color: 'black' },
    },
  },
  options
)

HTML Elements

Satori supports a limited subset of HTML and CSS features, due to its special use cases. In general, only these static and visible elements and properties that are implemented.

For example, the <input> HTML element, the cursor CSS property are not in consideration. And you can't use <style> tags or external resources via <link> or <script>.

Also, Satori does not guarantee that the SVG will 100% match the browser-rendered HTML output since Satori implements its own layout engine based on the SVG 1.1 spec.

You can find the list of supported HTML elements and their preset styles here.

Images

You can use <img> to embed images. However, width, and height attributes are recommended to set:

await satori(
  <img src="https://picsum.photos/200/300" width={200} height={300} />,
  options
)

When using background-image, the image will be stretched to fit the element by default if you don't specify the size.

If you want to render the generated SVG to another image format such as PNG, it would be better to use base64 encoded image data (or buffer) directly as props.src so no extra I/O is needed in Satori:

await satori(
  <img src="data:image/png;base64,..." width={200} height={300} />,
  // Or src={arrayBuffer}, src={buffer}
  options
)

CSS

Satori uses the same Flexbox layout engine as React Native, and it’s not a complete CSS implementation. However, it supports a subset of the spec that covers most common CSS features:

Property Property Expanded Supported Values Example
CSS Variables Supported, including --var-name declaration and var(--var-name) usage with fallback values Example
display flex, block, contents, none, -webkit-box, default to flex. Use flex, contents, or none for div elements with multiple child nodes.
position relative, static and absolute, default to relative
color Supported
margin
marginTopSupported
marginRightSupported
marginBottomSupported
marginLeftSupported
Position
topSupported
rightSupported
bottomSupported
leftSupported
Size
widthSupported
heightSupported
Min & max size
minWidthSupported except for min-content, max-content and fit-content
minHeightSupported except for min-content, max-content and fit-content
maxWidthSupported except for min-content, max-content and fit-content
maxHeightSupported except for min-content, max-content and fit-content
border
Width (borderWidth, borderTopWidth, ...)Supported
Style (borderStyle, borderTopStyle, ...)solid and dashed, default to solid
Color (borderColor, borderTopColor, ...)Supported
Shorthand (border, borderTop, ...)Supported, i.e. 1px solid gray
borderRadius
borderTopLeftRadiusSupported
borderTopRightRadiusSupported
borderBottomLeftRadiusSupported
borderBottomRightRadiusSupported
ShorthandSupported, i.e. 5px, 50% / 5px
cornerShape
Valuesround, squircle, square, bevel, scoop, notch, and superellipse()
Corner longhands (cornerTopLeftShape, cornerTopRightShape, ...)Supported
Side shorthands (cornerTopShape, cornerRightShape, ...)Supported. Corner shapes apply when the corresponding borderRadius is nonzero.
Flex
flexDirectioncolumn, row, row-reverse, column-reverse, default to row
flexWrapwrap, nowrap, wrap-reverse, default to nowrap
flexGrowSupported
flexShrinkSupported
flexBasisSupported except for auto
alignItemsstretch, center, flex-start, flex-end, baseline, normal, default to stretch
alignContentSupported
alignSelfSupported
justifyContentSupported
gapSupported
Font
fontFamilySupported
fontSizeSupported
fontWeightSupported
fontStyleSupported
fontFeatureSettingsSupported via HarfBuzz text shaping. Enables OpenType features like ligatures, small caps, stylistic sets, etc.
Text
tabSizeSupported
textAlignstart, end, left, right, center, justify, default to start
textIndentSupported, including negative values (hanging indent)
textTransformnone, lowercase, uppercase, capitalize, defaults to none
textOverflowclip, ellipsis, defaults to clip
textDecorationSupport line types underline and line-through, and styles dotted, dashed, double, solidExample
textShadowSupported
lineHeightSupported
letterSpacingSupported
whiteSpacenormal, pre, pre-wrap, pre-line, nowrap, defaults to normal
wordBreaknormal, break-all, break-word, keep-all, defaults to normal
textWrapwrap, balance, defaults to wrap
Background
backgroundColorSupported, single value
backgroundImagelinear-gradient, repeating-linear-gradient, radial-gradient, repeating-radial-gradient, url, single value
backgroundPositionSupport single value
backgroundSizeSupport cover, contain, auto, and two-value sizes i.e. 10px 20%Example
backgroundClipborder-box, text
backgroundRepeatrepeat, repeat-x, repeat-y, no-repeat, defaults to repeat
transform
Translate (translate, translateX, translateY)Supported
RotateSupported
Scale (scale, scaleX, scaleY)Supported
Skew (skew, skewX, skewY)Supported
transformOrigin Support one-value and two-value syntax (both relative and absolute values)
objectFit Supported Example
objectPosition Supports keywords (top, bottom, left, right, center), percentages (e.g., 25% 75%), lengths (e.g., 10px 20px), and mixed values (e.g., left 20%). Defaults to center (50% 50%). Example
opacity Supported
boxSizing Supported
boxShadow Supported
overflow visible and hidden, default to visible
filter Supported
backdropFilter Supports chained blur(), brightness(), contrast(), drop-shadow(), grayscale(), hue-rotate(), invert(), opacity(), saturate(), and sepia()
clipPath Supports circle(), ellipse(), inset(), polygon(), path(), and shape(). shape() supports move, line, hline, vline, curve, smooth, arc, and close commands. Example
lineClamp Supported when the text element uses display: block. For WebKit-style clamping, use display: -webkit-box with WebkitLineClamp. Example
Mask
maskImagelinear-gradient(...), radial-gradient(...), url(...)Example
maskPositionSupportedExample
maskSizeSupport two-value size i.e. 10px 20%Example
maskRepeatrepeat, repeat-x, repeat-y, no-repeat, defaults to repeatExample
WebkitTextStroke WebkitTextStrokeWidth Supported
WebkitTextStrokeColor Supported

Note:

  1. Three-dimensional transforms are not supported.
  2. There is no z-index support in SVG. Elements that come later in the document will be painted on top.
  3. calc isn't supported.
  4. currentColor support is only available for the color property.
  5. CSS variables (custom properties) are supported, including inheritance, fallback values, and nested variables.

Language and Typography

OpenType Features: Satori supports advanced typography features via HarfBuzz text shaping. Use the font-feature-settings CSS property to enable OpenType features such as:

Example:

<div style={{ fontFeatureSettings: '"smcp" 1, "liga" 0' }}>
  This Text Uses Small Caps
</div>

HarfBuzz also improves glyph shaping for complex scripts such as Arabic. Full Unicode bidirectional layout is not yet supported, so mixed LTR and RTL text may not follow browser ordering.

Fonts

Satori currently supports three font formats: TTF, OTF and WOFF. Note that WOFF2 is not supported at the moment. You must specify the font if any text is rendered with Satori, and pass the font data as ArrayBuffer (web) or Buffer (Node.js):

await satori(
  <div style={{ fontFamily: 'Inter' }}>Hello</div>,
  {
    width: 600,
    height: 400,
    fonts: [
      {
        name: 'Inter',
        data: inter,
        weight: 400,
        style: 'normal',
      },
      {
        name: 'Inter',
        data: interBold,
        weight: 700,
        style: 'normal',
      },
    ],
  }
)

Multiple fonts can be passed to Satori and used in fontFamily.

[!TIP] We recommend you define global fonts instead of creating a new object and pass it to satori for better performance, if your fonts do not change. Read it for more detail

Emojis

To render custom images for specific graphemes, you can use graphemeImages option to map the grapheme to an image source:

await satori(
  <div>Next.js is 🤯!</div>,
  {
    ...,
    graphemeImages: {
      '🤯': 'https://cdnjs.cloudflare.com/ajax/libs/twemoji/14.0.2/svg/1f92f.svg',
    },
  }
)

The image will be resized to the current font-size (both width and height) as a square.

Locales

Satori supports rendering text in different locales. You can specify the supported locales via the lang attribute:

await satori(
  <div lang="ja-JP">骨</div>
)

Same characters can be rendered differently in different locales, you can specify the locale when necessary to force it to render with a specific font and locale. Check out this example to learn more.

Supported locales are exported as the Locale enum type.

Dynamically Load Emojis and Fonts

Satori supports dynamically loading emoji images (grapheme pictures) and fonts. The loadAdditionalAsset function will be called when a text segment is rendered but missing the image or font:

await satori(
  <div>👋 你好</div>,
  {
    // `code` will be the detected language code, `emoji` if it's an Emoji, or `unknown` if not able to tell.
    // `segment` will be the content to render.
    loadAdditionalAsset: async (code: string, segment: string) => {
      if (code === 'emoji') {
        // if segment is an emoji
        return `data:image/svg+xml;base64,...`
      }

      // if segment is normal text
      return loadFontFromSystem(code)
    }
  }
)

Runtime Support

Satori can be directly used in browser, Node.js (>= 16), and Web Workers. It bundles its underlying WASM dependencies as base64-encoded strings and loads them at runtime.

If there is a limitation on dynamically loading WASM (e.g. Cloudflare Workers), you can use the Standalone Build which is mentioned below.

Standalone Build of Satori

Satori's standalone build doesn't include Yoga's WASM binary by default, and you need to load it manually before using Satori.

First, you need to download the yoga.wasm binary from Satori build and provide it yourself. Let's use fetch to load it directly from the CDN as an example:

import satori, { init } from 'satori/standalone'

const res = await fetch('https://unpkg.com/satori/yoga.wasm')
const yogaWasm = await res.arrayBuffer()

await init(yogaWasm)

// Now you can use satori as usual
const svg = await satori(...)

Of course, you can also load the yoga.wasm file from your local disk via fs.readFile in Node.js or other methods.

Font Embedding

By default, Satori renders the text as <path> in SVG, instead of <text>. That means it embeds the font path data as inlined information, so succeeding processes (e.g. render the SVG on another platform) don’t need to deal with font files anymore.

You can turn off this behavior by setting embedFont to false, and Satori will use <text> instead:

const svg = await satori(
  <div style={{ color: 'black' }}>hello, world</div>,
  {
    ...,
    embedFont: false,
  },
)

Pixel Grid Rounding

Set pointScaleFactor to control how layout values are rounded to the pixel grid. This parameter is passed directly to Yoga’s pointScaleFactor and improves rendering precision on high-DPI displays.

const svg = await satori(
  <div style={{ color: 'black' }}>hello, world</div>,
  {
    ...,
    pointScaleFactor: 2,
  },
)

Debug

To draw the bounding box for debugging, you can pass debug: true as an option:

const svg = await satori(
  <div style={{ color: 'black' }}>hello, world</div>,
  {
    ...,
    debug: true,
  },
)

Contribute

You can use the Vercel OG Image Playground to test and report bugs of Satori. Please follow our contribution guidelines before opening a Pull Request.


Author


satori (vercel/satori)

Searching Database…

14.1K ·GitHub
这是什么?Satori 是一个 TypeScript 库,能把 HTML 和 CSS 转换成 SVG:你用 JSX 风格的标记描述一个视觉效果,它就会为你渲染出对应的 SVG。
这是我在自己代码里调用的库,还是一个现成的应用?是一个库——你从 npm 安装它,然后在 JavaScript 或 TypeScript 里调用 satori(),通常用来生成 Open Graph 图片和社交分享卡片。它返回一个 SVG 字符串,基础示例中的尺寸是 600x400。
我怎么用它生成第一个 SVG?把 satori 包添加到你的项目里,为你的卡片写一个纯 JSX 元素,然后把它连同你的字体以及 width/height 选项一起传给 satori()。
我需要自己准备字体吗?需要——任何文字都要求你以 ArrayBuffer(浏览器)或 Buffer(Node.js)的形式传入字体:TTF、OTF 和 WOFF 都可以,WOFF2 不行。字体路径默认会嵌入到 SVG 里,所以查看者不需要字体文件。
我能用普通的 CSS 来设计卡片样式吗?只能用其中一部分:Satori 使用与 React Native 相同的 Flexbox 引擎,常用属性都可以用,但不支持 z-index、calc 和 3D 变换,而且渲染结果不保证和浏览器完全一致。
它能在哪里运行——只能跑在 Node.js 上,还是浏览器也行?可以直接在浏览器、Node.js(>= 16)和 Web Workers 中运行,它的 WASM 依赖以 base64 形式打包在内。限制动态加载 WASM 的环境(比如 Cloudflare Workers)则需要使用独立构建版本,并手动加载 yoga.wasm。
使用 JSX 语法需要安装 React 吗?不需要。元素必须是纯的、无状态的,所以 useState、useEffect 这类 React API 不受支持。Satori 自带一个无需 React 就能工作的实验性 JSX 运行时,而且在不使用转译器的情况下,你也可以直接传入带有 type、props 和 style 的普通对象。
卡片里可以放图片和表情符号吗?可以,通过 img 元素实现——要设置 width 和 height,而且如果你打算把 SVG 转成 PNG,最好使用 base64 格式的图片数据。表情符号没有内置字体:可以用 graphemeImages 选项把它们映射成图片,或者通过 loadAdditionalAsset 动态加载资源。
它支持连字或阿拉伯文这类高级排版特性吗?支持——HarfBuzz 文字整形引擎支持连字、小型大写字母和风格集等 OpenType 特性,并改善了阿拉伯文等复杂文字的整形效果。不过,混合从左到右和从右到左文字的完整双向排版目前还不支持。
Satori 能为我的 OG 图片输出 PNG 吗?不能,它只返回 SVG。想要 PNG 格式的 Open Graph 图片,Vercel 的 Open Graph Image Generation 就是基于 Satori 构建的,你还可以先用 Vercel OG Image Playground 在浏览器里测试效果。
Creator
vercel
Size
21.4 MB
Stars
14.1K
Age
4y
Commits · 90 Days