@basenative/og-image

中文文档
3.0.0发布于 2026/10/31Apache-2.0TypeScriptES Module构建来源已验证

运行时无关OG /社交分享PNG渲染器- SVG卡场景栅格化的resvg-wasm,与CDN+ kv缓存字体;运行在Cloudflare Workers、Node、Deno和Bun上

中文使用文档
文档目录17 节
  1. 如果您是从0.2.x升级,请先阅读本文
  2. 为什么
  3. 安装
  4. 快速启动
  5. 卡片
  6. brandCard(opts) → string (SVG)
  7. 字体覆盖是一个真正的限制
  8. OG_SIZE
  9. 字体
  10. 无KV缓存
  11. 提供WASM模块
  12. 运行时检测
  13. 缓存图像本身
  14. API
  15. 主题
  16. 错误
  17. 许可证

@ basenative/og-image

运行时不可知的OG /社会分享PNG渲染器- SVG卡场景栅格化 resvg-wasm,与CDN + kv缓存字体。运行在Cloudflare Workers, Node, Deno和Bun上。

部分 BaseNative 生态系统——基于信号的web运行时,基于原生HTML。

在请求时间渲染1200x630 png og:image, twitter:image,以及任意共享卡端点。

如果您是从0.2.x升级,请先阅读本文

renderPng -顿悟的入口点- 已移动 到一个单独的出口:

-import { renderPng } from "@basenative/og-image";
+import { renderPng } from "@basenative/og-image/satori";

和 satori 现在是一个 可选的对等依赖: 如果你自己安装 使用那条路径。在Cloudflare Workers上,你不能;使用 renderCard/renderSvg 而是从主条目,这就是这个版本存在的目的。

为什么

此软件包的每个版本,包括0.2.2 不能执行 Cloudflare所有工作人员,尽管是为他们写的。 satori 导入 harfbuzzjs (它的文本塑造器),以及harfbuzz的刻字加载器 按顺序问两个环境问题,工人回答错误。已复制 反对真正的工人 (wrangler dev, compatibility_date 2026-04-23, nodejs_compat):

  1. TypeError: Cannot read properties of undefined (reading 'href') - - - 加载程序将 WorkerGlobalScope,结束“浏览器工作器”,并读取 self.location.href。workerd定义 WorkerGlobalScope 和 self, 定义了 否 location.
  2. 垫片 location 在它之前,你会得到 ReferenceError: __dirname is not defined - - - - - - nodejs_compat 给工人一个 process.versions.node,所以 loader接着结束“Node”,然后开始寻找 hb.wasm 在文件系统上 那是不存在的。牧马人不捆绑该文件,其余的 分支从缓冲区编译WASM,嵌入器完全拒绝 (Wasm code generation disallowed by embedder ——也测量过)。

固定 (2) 需要 harfbuzzjs 接受铭文 Module.instantiateWasm 钩子是静态导入的 .wasm 可以上交。 hb.js 荣誉的钩子; harfbuzzjs输入呼叫 hb() 没有参数,所以没有下游包可以 供应。一个牧人 [alias] 不能站在两者之一中-别名 harfbuzzjs 重写它的子路径,所以替换不能再解析真实的 hb.js/hb.wasm.

所以修复是不需要一个文本整形器。 @resvg/resvg-wasm 它自己 整形,它是一个单一的静态 .wasm 工人们接受。卡现在是一个 SVG字符串,并且布局引擎是 text.js -算术超过测量的字形 进步,无处不在。

安装

npm install @basenative/og-image
# only if you use the satori entry point:
npm install satori

快速启动

// src/worker.js — Cloudflare Worker
import { renderCard, pngHeaders } from "@basenative/og-image";

export default {
  async fetch(request, env) {
    const png = await renderCard(
      {
        title: "Northside Electrical",
        subtitle: "Electrical — Manchester, NH",
        badge: "Licensed electrician",
        brand: "northside.example.com",
      },
      env,
    );
    return new Response(png, { headers: pngHeaders() });
  },
};

测量于 wrangler dev: ~730ms的冷隔离(两个字体提取加上WASM) 加热26-45ms。

卡片

brandCard(opts) → string (SVG)

通用实体卡:一个名称、一个限定行、一个可选的徽章和一个 品牌线钉在底部。

import { brandCard, renderSvg } from "@basenative/og-image";

const svg = brandCard({
  title: "Northside Electrical",
  titleFallback: "northside",          // used if `title` has no letter or digit
  subtitle: "Electrical — Manchester, NH",
  badge: "Licensed electrician",
  brand: "northside.example.com",
  theme: { accent: "#4EAF7C" },
  width: 1200,
  height: 630,
});
const png = await renderSvg(svg, env);

没有输入可以破坏布局。 每次运行都符合宽度预算,并且 整个堆栈安装到顶部填充和品牌线之间的高度。 一个空标题,一个200字符的标题,一个没有空格的标题,RTL脚本, 标记、控制字符和单独的代理都由测试覆盖,并且 所有渲染到有效的1200x630 PNG。

值得了解的文本处理:

  • 收缩贴合,然后包裹,最多三行,然后是省略号。
  • 柔软的优惠 后 -, /, _ 和 .,所以一个呆滞的名字在 greenleaf- / landscaping 而不是中间词。
  • Grapheme-安全截断 -永远不会发出一个单独的代理,这将使 文档无效的XML。
  • RTL 集合 direction="rtl" 当内容主要是RTL脚本时。
  • XML-非法字符被删除,不转义 -杂散控制 字符在数据库列中,否则会将一个坏行变成500 公共页面。

字体覆盖是一个真正的限制

光栅化器只绘制给定的字体缓冲区中存在的字形,和 默认是拉丁Inter。一个CJK, Devanagari或阿拉伯语标题措施和 正确布局,然后呈现为 .notdef 盒子。 brandCard 限制了 损害-它总是吸引 brand 行,所以牌仍然标识它的 主题-但如果你提供这些脚本,提供一个覆盖面:

await renderCard(card, env, {
  fonts: { urls: { 600: "https://…/NotoSansArabic-SemiBold.ttf" } },
});

titleFallback 覆盖完全没有单词字符的名称的较窄大小写 (仅限表情符号的业务名称): 相反,将绘制回退。

OG_SIZE

{ width: 1200, height: 630 } -高于Facebook的600x315最小值,正是 1.91:1的比例它的文件,和里面的2:1 .. 1:1窗口X需要为 summary_large_image.

字体

两个渲染路径需要两种格式,出错是无声的:

路径格式来源为什么
renderSvg / renderCardttf@expo-google-fonts/* (上游Google TTFs在不可变的npm版本)resvg的 fontdb 只解析原始数据。给它一个 .woff 和它加载 零面不画任何东西 -一个正确大小的卡与文本丢失。
@basenative/og-image/satoriwoff@fontsource/*satori解压WOFF本身;文件要小5倍。
import { defineFonts } from "@basenative/og-image";

defineFonts({
  family: "Inter",
  weights: [600, 800],
  format: "ttf",
  cdnVersion: "0.2.3",
  cacheBinding: "OG_CACHE", // KV binding name; `null` = deliberately none
  cacheKeyPrefix: "font:",
  urls: { 600: "https://…/Custom-SemiBold.ttf" }, // per-weight override
  buffers: { 600: bytes },                         // skip the network entirely
});

缓存键包括格式和CDN版本,因此由satori缓存的WOFF 路径永远不能服务于光栅化路径。

无KV缓存

字体是用 cf: { cacheTtl: 86400, cacheEverything: true },所以 Cloudflare自己的边缘缓存即使在没有KV的情况下也能吸收冷隔离失误 名称空间。通过 cacheBinding: null 说这是故意和沉默 警告。KV:

wrangler kv namespace create OG_CACHE
[[kv_namespaces]]
binding = "OG_CACHE"
id = "<id>"

提供WASM模块

包解析resvg .wasm 本身:静态导入 牧马人/esbuild (提前编译-工人拒绝编译 从缓冲区中读取WASM),以及在Node下读取的文件系统。如果两者都不适合你 运行时或打包器,把它交出来:

import wasm from "@resvg/resvg-wasm/index_bg.wasm"; // your bundler
import { provideResvgWasm } from "@basenative/og-image";

provideResvgWasm(wasm); // WebAssembly.Module, bytes, a Response, or a promise

在第一次渲染之前,在模块作用域中调用它。你所提供的战胜了 内置的分辨率,然后从来没有咨询。

运行时检测

detectRuntime() 被导出,因为它的检查顺序是整个 此版本的主题:

import { detectRuntime, isWorkerd, hasFilesystem } from "@basenative/og-image";

detectRuntime(); // "workerd" | "node" | "deno" | "bun" | "browser" | "unknown"
  • 在Node之前检查workerd,因为 nodejs_compat 使it报告 process.versions.node.
  • 在浏览器之前检查workerd,因为它定义了 WorkerGlobalScope 和 self。将它与真正的浏览器工作者区分开来的信号是 浏览器工作人员 有 一个 location.

缓存图像本身

pngHeaders() 默认情况下,将响应标记为一年不可变,因为 预期的寻址方案是内容寻址的:放入卡自己的哈希值 因此,重命名主题会更改URL,并且不会留下爬虫 拿着一张陈旧的卡片。如果您的URL不是内容寻址的,请传递 { immutable: false }.

API

出口目的
renderCard(card, env?, opts?)创建一个品牌卡片并栅格化。
renderSvg(svg, env?, opts?)栅格化任何SVG字符串。
brandCard(opts)在不渲染的情况下构建卡片场景。
pngHeaders(opts?)Content-Type + Cache-Control.
provideResvgWasm(source)显式地提供WASM模块。
defineFonts, loadFontBuffers, loadFonts, fontUrl, fontCacheKey字体。
detectRuntime, isWorkerd, hasFilesystem, canCompileWasmFromBytes环境。
escapeXml, num, rect, textLine, textBlock, headline, clippedLine, svgDocSVG原语,用于自定义场景。
graphemes, estimateWidth, isRtl, hasWordCharacter, truncateGraphemes, truncateToWidth, wrapToLines, fitText文本布局。
OgImageError用马槽扔 code.
box, text, tile, tileGrid, parseGrid, theme, defaultTheme, el, defaultPreset, articlePreset, scoreCardPreset, presetssatori场景DSL-仅限数据; 通过 /satori 条目。

@basenative/og-image/satori 另外出口 renderPng(scene, env?, opts?).

主题

brandCard({
  title: "Hello",
  theme: { bg: "#0a0a0a", fg: "#fafafa", accent: "#7c3aed", muted: "#71717a" },
});

令牌: bg, fg, accent, muted, tile, letter, green, yellow, absent, empty.

错误

OgImageError 带着马厩 code:

代码意义
satori-unsupported-on-this-runtime你打电话给工人的satori入口点。
wasm-source-unavailable既不是导入映射也不是 provideResvgWasm 产生了一个模块。
wasm-source-invalidprovideResvgWasm 被交给的东西不是一个模块,字节或 Response.
font-fetch-failedCDN拒绝了,或者没有TTF知道这个家族/重量。
no-fonts解析的权重列表是空的; 光栅化器什么也不画。

许可证

apache - 2.0

版本与发布记录

@basenative/og-image 共发布 11 个版本,首次发布于 2026/10/18;过去 12 个月发布了 11 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
3.0.0latest2026/10/312 天前
2.4.02026/10/268 天前
2.3.22026/10/2212 天前
2.3.12026/10/2212 天前
2.3.02026/10/2212 天前
2.2.02026/10/2113 天前

关于 @basenative/og-image 的常见问题

@basenative/og-image 是什么?

运行时无关OG /社交分享PNG渲染器- SVG卡场景栅格化的resvg-wasm,与CDN+ kv缓存字体;运行在Cloudflare Workers、Node、Deno和Bun上

如何安装 @basenative/og-image?

在项目目录运行 npm install @basenative/og-image,或使用 pnpm add / yarn add / bun add。

@basenative/og-image 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@basenative/og-image 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@basenative/og-image 可以商用吗?

@basenative/og-image 使用 Apache-2.0 许可。宽松许可:允许商业使用、修改和再分发,并附带明确的专利授权;分发时需保留许可与 NOTICE 文件,修改过的文件要注明改动。以上为概括,不构成法律意见,请以许可原文为准。

@basenative/og-image 还在维护吗?

最新版本 3.0.0 发布于 2026/10/31(2 天前);至今共发布 11 个版本,其中过去 12 个月发布了 11 个;首次发布于 2026/10/18。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。