
Zebkit 将 Chromium 引擎打包成可链接库
来源标题:Zebkit – Chromium as an embeddable library
Zebkit 把 Chromium 引擎做成一个可直接链接进应用的库,包含 Blink、V8、PDFium、FFmpeg 等组件,但不含 Chrome 浏览器或 Electron 运行时。它提供 Linux、macOS、Windows 统一 API,支持 C、C++、Rust、Python 和 JavaScript 调用,应用可保留自己的窗口与工具包。
以下正文由 AI 翻译自 Hacker News(原文版权归原站所有),可能存在偏差,重要内容请以原文为准。
Chromium 的引擎作为一个库——轻量、快速、随处可用、易于集成。在 Linux、macOS 和 Windows 上只有一套 API,几行代码即可接入。zebkit 把 Chromium 的引擎做成了一个你可以链接进自己应用的库:包含 Blink、带全部 JIT 层的 V8、PDFium、FFmpeg 以及 Chrome 的其余组成部分,但不含 Chrome 浏览器的任何部分,也不含 Electron 的运行时。你的应用始终是你的:可以用 C、C++、Rust、Python 或 JavaScript 编写,拥有自己的窗口和工具包。需要网页时,加一个 zebkit 视图;需要 PDF、媒体、压缩或加密时,调用 zebkit 的 API:可用 C,也可通过 Rust、Python 和 JavaScript 绑定(zebkit-js,以及应用自身页面中的 zebkit.*)。
获取引擎
一个包就是一个目录,内含库、辅助程序、引擎数据和 include/。你可以解压某个 release 的归档(GitHub releases 中的 zebkit-<version>-<target>.tar.xz),也可以自行构建:
mise run r # dist/release(Linux)、dist/release-macos(macOS)、dist/release-windows;首次构建需要数小时
每个归档还包含为对应系统构建的工具,与库放在一起,解压后即可直接运行:zebkit-run、zebkit-js 和 zebkit-mcp(Windows 上为 .exe,同样在沙箱中运行)。在安装了完整 Xcode 的 Apple silicon Mac 上,scripts/dist.sh release 会在本地把引擎构建到 dist/release。然后运行 scripts/example.sh macos-basic,即可构建并启动 C 嵌入示例,位于 dist/example/Zebkit Embedded Browser.app。构建 Chromium 源码树约需 30 GB 空间(见 docs/BUILD.md)。下文中 $PKG 指包目录。
zebkit 是一个库:你的程序保留自己的 main() 并调用它。下面这个程序把 PDF 的第一页渲染为 PNG 并读取其文本,用的是 zebkit 内置的 PDFium 和图像编码器,无需安装其他东西:
#include <stdio.h>
#include <zebkit/pdf.h>
#include <zebkit/services.h>
/* Your program, your main(): zebkit is just a library it calls. */
int main(int argc, char **argv) {
char *error = NULL;
zebkit_pdf *pdf = zebkit_pdf_open_file(argv[1], NULL, &error);
if (!pdf) {
fprintf(stderr, "%s\n", error);
return 1;
}
/* The first page as a PNG, at twice its size. */
zebkit_pdf_render_options options = {.scale = 2, .background = 0xffffffff};
int width, height;
uint8_t *rgba = zebkit_pdf_render(pdf, 0, &options, &width, &height, &error);
size_t size;
uint8_t *png = zebkit_image_encode(ZEBKIT_IMAGE_PNG, rgba, width, height, 0, &size, &error);
fwrite(png, 1, size, fopen("page1.png", "wb"));
/* And its text. */
char *text = zebkit_pdf_text(pdf, 0, &error);
printf("%d pages; page 1 says: %.80s\n", zebkit_pdf_page_count(pdf), text);
zebkit_free(text), zebkit_free(png), zebkit_free(rgba);
zebkit_pdf_close(pdf);
return 0;
}
cc thumb.c -I$PKG/include -L$PKG -l:libzebkit.so.1 -Wl,-rpath,$PKG -o thumb # Linux
cc thumb.c -I$PKG/include -F$PKG -framework Zebkit -Wl,-rpath,$PKG -o thumb # macOS
./thumb report.pdf # 2 pages; page 1 says: Quarterly report ...
PDF API、压缩、加密、URL、图像编解码器、ICU 以及引擎内部的其他库(zebkit_vendor_*,包括 FFmpeg 的库)都不需要引擎循环(见 docs/API.md);只有通过引擎网络栈发起的 HTTP 请求和网页需要。
网页需要引擎运行。这时由引擎来跑循环:程序用 zebkit_main() 启动它,并在其回调中工作。自带循环的程序(GTK、Qt、AppKit、winit)用 zebkit_init() 启动,并调用 zebkit_pump()(见 examples/native-host)。
#include <stdio.h>
#include <zebkit/zebkit.h>
static void printed(zebkit_view *view, const char *json, const char *error, void *data) {
printf("%s\n", json ? json : error);
zebkit_quit();
}
static void loaded(zebkit_view *view, zebkit_load_event event, const char *url, void *data) {
if (event == ZEBKIT_LOAD_FINISHED)
zebkit_view_evaluate(view, "document.title", printed, NULL);
}
static void ready(void *data) {
zebkit_view *view = zebkit_view_new("https://example.com/", 1024, 768);
zebkit_view_on_load(view, loaded, NULL);
}
int main(int argc, char **argv) {
return zebkit_main(argc, argv, ready, NULL);
}
cc app.c -I$PKG/include -L$PKG -l:libzebkit.so.1 -Wl,-rpath,$PKG -o app # Linux
cc app.c -I$PKG/include -F$PKG -framework Zebkit -Wl,-rpath,$PKG -o app # macOS
clang-cl /MT app.c /I%PKG%\include %PKG%\zebkit.dll.lib %PKG%\zebkit_sandbox.lib # Windows
./app --remote-debugging-port=9222 # with CDP for Puppeteer and Playwright
在 Windows 上,可执行文件参与 Chromium 的沙箱:它链接 zebkit_sandbox.lib,并在 main() 中首先调用 zebkit_win_startup()(见 docs/WINDOWS.md)。如果没有非特权用户命名空间,引擎会拒绝启动并说明原因:安装其辅助程序,或在自有的容器中添加 --no-sandbox(见 docs/SANDBOX.md)。
应用通过自定义 scheme 提供自己的页面,并赋予页面可调用的函数;除非被授予,页面与 zebkit 毫无关联。
/* The page calls zebkit.app.greet("Ada"). */
static void greet(zebkit_view *view, const char *origin, const char *args_json,
zebkit_call *call, void *data) {
char answer[256];
snprintf(answer, sizeof answer, "{\"hello\": %s}", args_json);
zebkit_call_resolve(call, answer);
}
/* app://main/... from ./ui, in ranges for media. A real handler refuses "..". */
static void serve(zebkit_request *request, void *data) {
char path[1024];
snprintf(path, sizeof path, "ui%s", zebkit_request_url(request) + sizeof "app://main" - 1);
zebkit_request_respond_file(request, path, NULL);
}
static void closed(zebkit_view *view, void *data) { zebkit_quit(); }
static void ready(void *data) {
zebkit_view *view = zebkit_view_new("app://main/index.html", 1024, 768);
zebkit_view_grant(view, "app://main", ZEBKIT_ALLOW_APP | ZEBKIT_ALLOW_PDF, NULL);
zebkit_view_bind(view, "greet", greet, NULL);
zebkit_view_on_close(view, closed, NULL);
}
int main(int argc, char **argv) {
zebkit_scheme_register("app", ZEBKIT_SCHEME_STANDARD | ZEBKIT_SCHEME_SECURE | ZEBKIT_SCHEME_FETCH);
zebkit_scheme_handle("app", serve, NULL);
return zebkit_main(argc, argv, ready, NULL);
}
(原文较长,此处译出前 6000 字,完整内容请点「打开原文」。)
正文由 探How 从来源站点 RSS 同步,内容未经改写;遇到排版缺失或需要图片、视频时请以原文为准。