Satori:将 HTML 和 CSS 转换为 SVG 的启发式库。
注意
要在你的项目中使用 Satori 生成 PNG 图像,如开放图谱图像和社交卡片,请查看我们的公告和Vercel 的开放图谱图像生成 →
要在 Next.js 中使用它,请查看 Next.js 开放图谱图像生成模板 →
概述
Satori 支持 JSX 语法,这使得它非常易于使用。以下是基本用法概述:
// api.jsx
import satori from 'satori'
const svg = await satori(
<div style={{ color: 'black' }}>你好,世界</div>,
{
width: 600,
height: 400,
fonts: [
{
name: 'Roboto',
// 使用 `fs`(仅限 Node.js)或 `fetch` 将字体读取为 Buffer/ArrayBuffer 并在此处提供 `data`。
data: robotoArrayBuffer,
weight: 400,
style: 'normal',
},
],
},
)
Satori 将把元素渲染成一个 600×400 的 SVG,并返回 SVG 字符串:
'<svg ...><path d="..." fill="black"></path></svg>'
在底层,它处理布局计算、字体、排版等,生成与浏览器中完全相同的 HTML 和 CSS 的 SVG。
文档
JSX
Satori 只接受纯粹且无状态的 JSX 元素。你可以使用 HTML 元素的子集(见下文),或自定义 React 组件,但不支持 React API,如 useState
、useEffect
、dangerouslySetInnerHTML
。
不使用 JSX
如果你没有启用 JSX 转译器,你可以直接传递具有 type
、props.children
和 props.style
(以及其他属性)的类 React 元素对象:
await satori(
{
type: 'div',
props: {
children: '你好,世界',
style: { color: 'black' },
},
},
options
)
HTML 元素
由于其特殊用例,Satori 支持有限的 HTML 和 CSS 功能子集。通常,只实现了静态和可见的元素和属性。
例如,<input>
HTML 元素、cursor
CSS 属性不在考虑范围内。而且你不能使用 <style>
标签或通过 <link>
或 <script>
使用外部资源。
此外,Satori 不保证 SVG 会 100% 匹配浏览器渲染的 HTML 输出,因为 Satori 基于 SVG 1.1 规范实现了自己的布局引擎。
你可以在这里找到支持的 HTML 元素及其预设样式列表。
图像
你可以使用 <img>
嵌入图像。但建议设置 width
和 height
属性:
await satori(
<img src="https://picsum.photos/200/300" width={200} height={300} />,
options
)
使用 background-image
时,如果你没有指定尺寸,图像默认会被拉伸以适应元素。
如果你想将生成的 SVG 渲染为其他图像格式(如 PNG),最好直接使用 base64 编码的图像数据(或 buffer)作为 props.src
,这样 Satori 就不需要额外的 I/O:
await satori(
<img src="https://raw.githubusercontent.com/vercel/satori/main/data:image/png;base64,..." width={200} height={300} />,
// 或 src={arrayBuffer}, src={buffer}
options
)
CSS
Satori 使用与 React Native 相同的 Flexbox 布局引擎,它不是一个完整的 CSS 实现。然而,它支持规范的一个子集,涵盖了大多数常用的 CSS 功能:
注意:- 不支持三维变换。
- SVG 中不支持
z-index
。文档中较后出现的元素将被绘制在上层。 - 所有元素的
box-sizing
都设置为border-box
。 - 不支持
calc
。 overflow: hidden
和transform
不能同时使用。currentcolor
仅支持color
属性。
语言和排版
目前不支持字距调整、连字等高级排版功能和其他 OpenType 特性。
同样不支持从右到左的语言。
字体
Satori 目前支持三种字体格式:TTF、OTF 和 WOFF。请注意,目前不支持 WOFF2。使用 Satori 渲染任何文本时必须指定字体,并将字体数据作为 ArrayBuffer(网页)或 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',
},
],
}
)
可以向 Satori 传递多种字体并在 fontFamily
中使用。
表情符号
要为特定字素渲染自定义图像,可以使用 graphemeImages
选项将字素映射到图像源:
await satori(
<div>Next.js is 🤯!</div>,
{
...,
graphemeImages: {
'🤯': 'https://cdnjs.cloudflare.com/ajax/libs/twemoji/14.0.2/svg/1f92f.svg',
},
}
)
图像将被调整为当前字体大小(宽度和高度)的正方形。
语言环境
Satori 支持在不同语言环境中渲染文本。您可以通过 lang
属性指定支持的语言环境:
await satori(
<div lang="ja-JP">骨</div>
)
相同的字符在不同的语言环境中可能会有不同的渲染方式,您可以在必要时指定语言环境,以强制使用特定字体和语言环境进行渲染。查看此示例了解更多信息。
支持的语言环境作为 Locale
枚举类型导出。
动态加载表情符号和字体
Satori 支持动态加载表情符号图像(字素图片)和字体。当渲染文本段但缺少图像或字体时,将调用 loadAdditionalAsset
函数:
await satori(
<div>👋 你好</div>,
{
// `code` 将是检测到的语言代码,如果是表情符号则为 `emoji`,如果无法判断则为 `unknown`。
// `segment` 将是要渲染的内容。
loadAdditionalAsset: async (code: string, segment: string) => {
if (code === 'emoji') {
// 如果 segment 是表情符号
return `data:image/svg+xml;base64,...`
}
// 如果 segment 是普通文本
return loadFontFromSystem(code)
}
}
)
运行时和 WASM
Satori 可以在浏览器、Node.js (>= 16) 和 Web Workers 中使用。
默认情况下,Satori 在浏览器运行时依赖于 asm.js,在 Node.js 中依赖于原生模块。但是,您可以选择通过导入 satori/wasm
来加载 WASM,并向 Satori 提供初始化的 Yoga WASM 模块实例:
import satori, { init } from 'satori/wasm'
import initYoga from 'yoga-wasm-web'
const yoga = initYoga(await fetch('/yoga.wasm').then(res => res.arrayBuffer()))
init(yoga)
await satori(...)
在浏览器或 Node.js 环境中运行时,需要托管 WASM 文件并在初始化之前获取。asm.js 可以与库一起打包。在这种情况下,WASM 应该更快。
在 Node.js 服务器上运行时,原生模块应该更快。但是,有些 Node.js 环境不支持原生模块(例如 StackBlitz 的 WebContainers),或者其他支持 WASM 的 JS 运行时(例如 Vercel 的 Edge Runtime、Cloudflare Workers 或 Deno)。
此外,asm.js、原生模块和 WASM 之间还有其他差异,如安全性和兼容性。
总的来说,每种选择都有许多权衡,最好选择最适合您的用例的方案。
字体嵌入
默认情况下,Satori 将文本渲染为 SVG 中的 <path>
,而不是 <text>
。这意味着它将字体路径数据作为内联信息嵌入,因此后续处理(例如在另一个平台上渲染 SVG)不需要再处理字体文件。
您可以通过将 embedFont
设置为 false
来关闭此行为,Satori 将使用 <text>
替代:
const svg = await satori(
<div style={{ color: 'black' }}>hello, world</div>,
{
...,
embedFont: false,
},
)
调试
要绘制用于调试的边界框,可以传递 debug: true
作为选项:
const svg = await satori(
<div style={{ color: 'black' }}>hello, world</div>,
{
...,
debug: true,
},
)
贡献
您可以使用 Vercel OG Image Playground 来测试和报告 Satori 的错误。在开启拉取请求之前,请遵循我们的贡献指南。
作者
- Shu Ding (@shuding_)
属性 | 属性扩展 | 支持的值 | 示例 |
---|---|---|---|
display | none 和 flex ,默认为 flex | ||
position | relative 和 absolute ,默认为 relative | ||
color | 支持 | ||
margin | |||
marginTop | 支持 | ||
marginRight | 支持 | ||
marginBottom | 支持 | ||
marginLeft | 支持 | ||
位置 | |||
top | 支持 | ||
right | 支持 | ||
bottom | 支持 | ||
left | 支持 | ||
尺寸 | |||
width | 支持 | ||
height | 支持 | ||
最小和最大尺寸 | |||
minWidth | 支持,除了 min-content 、max-content 和 fit-content | ||
minHeight | 支持,除了 min-content 、max-content 和 fit-content | ||
maxWidth | 支持,除了 min-content 、max-content 和 fit-content | ||
maxHeight | 支持,除了 min-content 、max-content 和 fit-content | ||
border | |||
宽度 (borderWidth , borderTopWidth , ...) | 支持 | ||
样式 (borderStyle , borderTopStyle , ...) | solid 和 dashed ,默认为 solid | ||
颜色 (borderColor , borderTopColor , ...) | 支持 | ||
简写 (border , borderTop , ...) | 支持,如 1px solid gray | ||
borderRadius | |||
borderTopLeftRadius | 支持 | ||
borderTopRightRadius | 支持 | ||
borderBottomLeftRadius | 支持 | ||
borderBottomRightRadius | 支持 | ||
简写 | 支持,如 5px 、50% / 5px | ||
弹性布局 | |||
flexDirection | column 、row 、row-reverse 、column-reverse ,默认为row | ||
flexWrap | wrap 、nowrap 、wrap-reverse ,默认为wrap | ||
flexGrow | 支持 | ||
flexShrink | 支持 | ||
flexBasis | 支持,除了auto | ||
alignItems | stretch 、center 、flex-start 、flex-end 、baseline 、normal ,默认为stretch | ||
alignContent | 支持 | ||
alignSelf | 支持 | ||
justifyContent | 支持 | ||
gap | 支持 | ||
字体 | |||
fontFamily | 支持 | ||
fontSize | 支持 | ||
fontWeight | 支持 | ||
fontStyle | 支持 | ||
文本 | |||
tabSize | 支持 | ||
textAlign | start 、end 、left 、right 、center 、justify ,默认为start | ||
textTransform | none 、lowercase 、uppercase 、capitalize ,默认为none | ||
textOverflow | clip 、ellipsis ,默认为clip | ||
textDecoration | 支持线型underline 和line-through ,以及样式dotted 、dashed 、solid | 示例 | |
textShadow | 支持 | ||
lineHeight | 支持 | ||
letterSpacing | 支持 | ||
whiteSpace | normal 、pre 、pre-wrap 、pre-line 、nowrap ,默认为normal | ||
wordBreak | normal 、break-all 、break-word 、keep-all ,默认为normal | ||
textWrap | wrap 、balance ,默认为wrap | ||
背景 | |||
backgroundColor | 支持,单一值 | ||
backgroundImage | linear-gradient 、radial-gradient 、url ,单一值 | ||
backgroundPosition | 支持单一值 | ||
backgroundSize | 支持两个值的大小,如 `10px 20%` | ||
backgroundClip | border-box 、text | ||
backgroundRepeat | repeat 、repeat-x 、repeat-y 、no-repeat ,默认为repeat | ||
transform | |||
平移(translate 、translateX 、translateY ) | 支持 | ||
旋转 | 支持 | ||
缩放(scale 、scaleX 、scaleY ) | 支持 | ||
倾斜(skew 、skewX 、skewY ) | 支持 | ||
transformOrigin | 支持一值和两值语法(相对和绝对值都支持) | ||
objectFit | contain 、cover 、none ,默认为none | ||
opacity | 支持 | ||
boxShadow | 支持 | ||
overflow | visible 和hidden ,默认为visible | ||
filter | 支持 | ||
clipPath | 支持 | 示例 | |
lineClamp | 支持 | 示例 | |
遮罩 | |||
maskImage | linear-gradient(...) 、radial-gradient(...) 、url(...) | 示例 | |
maskPosition | 支持 | 示例 | |
maskSize | 支持两个值的大小,如 `10px 20%` | 示例 | |
maskRepeat | repeat 、repeat-x 、repeat-y 、no-repeat ,默认为 repeat | 示例 |