|
| 1 | +--- |
| 2 | +sidebar_position: 8 |
| 3 | +description: 轻量化 HTML 绘图 |
| 4 | +--- |
| 5 | + |
| 6 | +# 轻量化 HTML 绘图 |
| 7 | + |
| 8 | +图片是机器人交互中不可或缺的一部分,对于信息展示的直观性、美观性有很大的作用。 |
| 9 | +基于 PIL 直接绘制图片具有良好的性能和存储开销,但是难以调试、维护过程式的绘图代码。 |
| 10 | +使用浏览器渲染类插件可以方便地绘制网页,且能够直接通过 JS 对网页效果进行编程,但是它占用的存储和内存空间相对可观。 |
| 11 | + |
| 12 | +NoneBot 提供的 `nonebot-plugin-htmlkit` 提供了另一种基于 HTML 和 CSS 语法的轻量化绘图选择:它基于 `litehtml` 解析库,无须安装额外的依赖即可使用,没有进程间通信带来的额外开销,且在支持 `webp` `avif` 等丰富图片格式的前提下,安装用的 wheel 文件大小仅有约 10 MB。 |
| 13 | + |
| 14 | +作为粗略的性能参考,在一台 Ryzen 7 9700X 的 Windows 电脑上,渲染 [PEP 7](https://peps.python.org/pep-0007/) 的 HTML 页面(分辨率为 800x5788,大小约 1.4MB,从本地文件系统读取 CSS)大约需要 100ms,每个渲染任务内存最高占用约为 40MB. |
| 15 | + |
| 16 | +## 安装插件 |
| 17 | + |
| 18 | + |
| 19 | +在使用前请先安装 `nonebot-plugin-htmlkit` 插件至项目环境中,可参考[获取商店插件](../tutorial/store.mdx#安装插件)来了解并选择安装插件的方式。如: |
| 20 | + |
| 21 | +在**项目目录**下执行以下命令: |
| 22 | + |
| 23 | +```bash |
| 24 | +nb plugin install nonebot-plugin-htmlkit |
| 25 | +``` |
| 26 | + |
| 27 | +`nonebot-plugin-htmlkit` 插件目前兼容以下系统架构: |
| 28 | + |
| 29 | +- Windows x64 |
| 30 | +- macOS arm64(M-系列芯片) |
| 31 | +- Linux x64 (非 Alpine 等 musl 系发行版) |
| 32 | +- Linux arm64 (非 Alpine 等 musl 系发行版) |
| 33 | + |
| 34 | +:::caution 访问网络内容 |
| 35 | + |
| 36 | +如果需要访问网络资源(如 http(s) 网页内容),NoneBot 需要客户端型驱动器(Forward)。内置的驱动器有 `~httpx` 与 `~aiohttp`。 |
| 37 | + |
| 38 | +详见[选择驱动器](../advanced/driver.md)。 |
| 39 | + |
| 40 | +::: |
| 41 | + |
| 42 | +## 使用插件 |
| 43 | + |
| 44 | +### 加载插件 |
| 45 | + |
| 46 | +在使用本插件前同样需要使用 `require` 方法进行**加载**并**导入**需要使用的方法,可参考 [跨插件访问](../advanced/requiring.md) 一节进行了解,如: |
| 47 | + |
| 48 | +```python |
| 49 | +from nonebot import require |
| 50 | + |
| 51 | +require("nonebot_plugin_htmlkit") |
| 52 | + |
| 53 | +from nonebot_plugin_htmlkit import html_to_pic, md_to_pic, template_to_pic, text_to_pic |
| 54 | +``` |
| 55 | + |
| 56 | +插件会自动使用[配置中的参数](#配置-fontconfig)初始化 `fontconfig` 以提供字体查找功能。 |
| 57 | + |
| 58 | +### 渲染 API |
| 59 | + |
| 60 | +`nonebot-plugin-htmlkit` 主要提供以下**异步**渲染函数: |
| 61 | + |
| 62 | +#### html_to_pic |
| 63 | + |
| 64 | +``` python |
| 65 | +async def html_to_pic( |
| 66 | + html: str, |
| 67 | + *, |
| 68 | + base_url: str = "", |
| 69 | + dpi: float = 144.0, |
| 70 | + max_width: float = 800.0, |
| 71 | + device_height: float = 600.0, |
| 72 | + default_font_size: float = 12.0, |
| 73 | + font_name: str = "sans-serif", |
| 74 | + allow_refit: bool = True, |
| 75 | + image_format: Literal["png", "jpeg"] = "png", |
| 76 | + jpeg_quality: int = 100, |
| 77 | + lang: str = "zh", |
| 78 | + culture: str = "CN", |
| 79 | + img_fetch_fn: ImgFetchFn = combined_img_fetcher, |
| 80 | + css_fetch_fn: CSSFetchFn = combined_css_fetcher, |
| 81 | + urljoin_fn: Callable[[str, str], str] = urllib3.parse.urljoin, |
| 82 | +) -> bytes: |
| 83 | + ... |
| 84 | +``` |
| 85 | + |
| 86 | +最核心的渲染函数。 |
| 87 | + |
| 88 | +`base_url` 和 `urljoin_fn` 控制着传入 `image_fetch_fn` 和 `css_fetch_fn` 回调的 url 内容。 |
| 89 | + |
| 90 | +`allow_refit` 如果为真,渲染时会自动缩小产出图片的宽度到最适合的宽度,否则必定产出 `max_width` 宽度的图片。 |
| 91 | + |
| 92 | +`max_width` 与 `device_height` 会在 `@media` 判断中被使用。 |
| 93 | + |
| 94 | +`img_fetch_fn` 预期为一个异步可调用对象(函数),接收图片 url 并返回对应 url 的 jpeg 或 png 二进制数据(`bytes`),可在拒绝加载时返回 `None`. |
| 95 | + |
| 96 | +`css_fetch_fn` 预期为一个异步可调用对象(函数),接收目标 CSS url 并返回对应 url 的 CSS 文本(`str`),可在拒绝加载时返回 `None`. |
| 97 | + |
| 98 | +以下为辅助的封装函数,关键字参数若未特殊说明均与 `html_to_pic` 含义相同。 |
| 99 | + |
| 100 | +#### text_to_pic |
| 101 | + |
| 102 | +```python |
| 103 | +async def text_to_pic( |
| 104 | + text: str, |
| 105 | + css_path: str = "", |
| 106 | + *, |
| 107 | + max_width: int = 500, |
| 108 | + allow_refit: bool = True, |
| 109 | + image_format: Literal["png", "jpeg"] = "png", |
| 110 | + jpeg_quality: int = 100, |
| 111 | +) -> bytes: |
| 112 | + ... |
| 113 | +``` |
| 114 | + |
| 115 | +可用于渲染多行文本。 |
| 116 | + |
| 117 | +`text` 会被放置于 `<div id="main" class="main-box"> <div class="text">` 中,可据此编写 CSS 来改变文本表现。 |
| 118 | + |
| 119 | +#### md_to_pic |
| 120 | + |
| 121 | +```python |
| 122 | +async def md_to_pic( |
| 123 | + md: str = "", |
| 124 | + md_path: str = "", |
| 125 | + css_path: str = "", |
| 126 | + *, |
| 127 | + max_width: int = 500, |
| 128 | + img_fetch_fn: ImgFetchFn = combined_img_fetcher, |
| 129 | + allow_refit: bool = True, |
| 130 | + image_format: Literal["png", "jpeg"] = "png", |
| 131 | + jpeg_quality: int = 100, |
| 132 | +) -> bytes: |
| 133 | + ... |
| 134 | +``` |
| 135 | + |
| 136 | +可用于渲染 Markdown 文本。默认为 GitHub Markdown Light 风格,支持基于 `pygments` 的代码高亮。 |
| 137 | + |
| 138 | +`md` 和 `md_path` 二选一,前者设置时应为 Markdown 的文本,后者设置时应为指向 Markdown 文本文件的路径。 |
| 139 | + |
| 140 | +#### template_to_pic |
| 141 | + |
| 142 | +```python |
| 143 | +async def template_to_pic( |
| 144 | + template_path: str | PathLike[str] | Sequence[str | PathLike[str]], |
| 145 | + template_name: str, |
| 146 | + templates: Mapping[Any, Any], |
| 147 | + filters: None | Mapping[str, Any] = None, |
| 148 | + *, |
| 149 | + max_width: int = 500, |
| 150 | + device_height: int = 600, |
| 151 | + base_url: str | None = None, |
| 152 | + img_fetch_fn: ImgFetchFn = combined_img_fetcher, |
| 153 | + css_fetch_fn: CSSFetchFn = combined_css_fetcher, |
| 154 | + allow_refit: bool = True, |
| 155 | + image_format: Literal["png", "jpeg"] = "png", |
| 156 | + jpeg_quality: int = 100, |
| 157 | +) -> bytes: |
| 158 | + ... |
| 159 | +``` |
| 160 | + |
| 161 | +渲染 jinja2 模板。 |
| 162 | + |
| 163 | +`template_path` 为 jinja2 环境的路径,`template_name` 是环境中要加载模板的名字,`templates` 为传入模板的参数,`filters` 为过滤器名 -> 自定义过滤器的映射。 |
| 164 | + |
| 165 | +### 控制外部资源获取 |
| 166 | + |
| 167 | +通过传入 `img_fetch_fn` 与 `css_fetch_fn`,我们可以在实际访问资源前进行审查,修改资源的来源,或是对 IO 操作进行缓存。 |
| 168 | + |
| 169 | +`img_fetch_fn` 预期为一个异步可调用对象(函数),接收图片 url 并返回对应 url 的 jpeg 或 png 二进制数据(`bytes`),可在拒绝加载时返回 `None`. |
| 170 | + |
| 171 | +`css_fetch_fn` 预期为一个异步可调用对象(函数),接收目标 CSS url 并返回对应 url 的 CSS 文本(`str`),可在拒绝加载时返回 `None`. |
| 172 | + |
| 173 | +如果你想要禁用外部资源加载/只从文件系统加载/只从网络加载,可以使用 `none_fetcher` `filesystem_***_fetcher` `network_***_fetcher`。 |
| 174 | + |
| 175 | +默认的 fetcher 行为(对于 `file://` 从文件系统加载,其余从网络加载)位于 `combined_***_fetcher`,可以通过对其封装实现缓存等操作。 |
| 176 | + |
| 177 | +## 配置项 |
| 178 | + |
| 179 | +### 配置 fontconfig |
| 180 | + |
| 181 | +`htmlkit` 使用 `fontconfig` 查找字体,请参阅 [`fontconfig 用户手册`](https://fontconfig.pages.freedesktop.org/fontconfig/fontconfig-user) 了解环境变量的具体含义、如何通过编写配置文件修改字体配置等。 |
| 182 | + |
| 183 | +#### fontconfig_file |
| 184 | + |
| 185 | +- **类型**: `str | None` |
| 186 | +- **默认值**: `None` |
| 187 | + |
| 188 | +覆盖默认的配置文件路径。 |
| 189 | + |
| 190 | +#### fontconfig_path |
| 191 | + |
| 192 | +- **类型**: `str | None` |
| 193 | +- **默认值**: `None` |
| 194 | + |
| 195 | +覆盖默认的配置目录。 |
| 196 | + |
| 197 | +#### fontconfig_sysroot |
| 198 | + |
| 199 | +- **类型**: `str | None` |
| 200 | +- **默认值**: `None` |
| 201 | + |
| 202 | +覆盖默认的 sysroot。 |
| 203 | + |
| 204 | +#### fc_debug |
| 205 | + |
| 206 | +- **类型**: `str | None` |
| 207 | +- **默认值**: `None` |
| 208 | + |
| 209 | +设置 Fontconfig 的 debug 级别。 |
| 210 | + |
| 211 | +#### fc_dbg_match_filter |
| 212 | + |
| 213 | +- **类型**: `str | None` |
| 214 | +- **默认值**: `None` |
| 215 | + |
| 216 | +当 `FC_DEBUG` 设置为 `MATCH2` 时,过滤 debug 输出。 |
| 217 | + |
| 218 | +#### fc_lang |
| 219 | + |
| 220 | +- **类型**: `str | None` |
| 221 | +- **默认值**: `None` |
| 222 | + |
| 223 | +设置默认语言,否则从 `LOCALE` 环境变量获取。 |
| 224 | + |
| 225 | +#### fontconfig_use_mmap |
| 226 | + |
| 227 | +- **类型**: `str | None` |
| 228 | +- **默认值**: `None` |
| 229 | + |
| 230 | +是否使用 `mmap(2)` 读取字体缓存。 |
0 commit comments