Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,73 @@ htmlrender_connect_over_cdp = "http://127.0.0.1:9222"
htmlrender_connect="ws://playwright:3000"
```

### 远程浏览器使用说明

当使用远程浏览器(通过 `htmlrender_connect` 或 `htmlrender_connect_over_cdp` 配置)时,本地文件系统的 `file://` 协议将无法访问。此时需要:

#### 1. 使用 `html_to_pic` 函数

可以直接传入不同的 URL 协议:

```python
from nonebot_plugin_htmlrender import html_to_pic

# 使用 about:blank(推荐用于纯 HTML 内容)
pic = await html_to_pic(
html="<html><body><h1>Hello</h1></body></html>",
template_path="about:blank"
)

# 使用 data URL
pic = await html_to_pic(
html="<html><body><h1>Hello</h1></body></html>",
template_path="data:text/html,<html></html>"
)

# 使用 HTTP URL(需要有可访问的 Web 服务器提供资源)
pic = await html_to_pic(
html="<html><body><h1>Hello</h1></body></html>",
template_path="http://your-server.com/base/"
)
```

#### 2. 使用 `template_to_pic` 函数

需要通过 `pages` 参数的 `base_url` 字段指定浏览器使用的基础 URL:

```python
from nonebot_plugin_htmlrender import template_to_pic
from pathlib import Path

template_path = str(Path(__file__).parent / "templates")

# 对于远程浏览器,使用 about:blank 或 HTTP URL
pic = await template_to_pic(
template_path=template_path, # 本地模板路径(用于 jinja2 加载)
template_name="my_template.html",
templates={"data": "value"},
pages={
"viewport": {"width": 600, "height": 300},
"base_url": "about:blank", # 浏览器使用的基础 URL
},
)

# 如果模板中有相对路径的资源(如图片、CSS),需要使用 HTTP URL
pic = await template_to_pic(
template_path=template_path,
template_name="my_template.html",
templates={"data": "value"},
pages={
"viewport": {"width": 600, "height": 300},
"base_url": "http://your-server.com/static/",
},
)
```

**注意**:
- `template_path` 参数始终是本地文件系统路径,用于 jinja2 模板引擎加载模板文件
- `base_url` 参数是浏览器使用的基础 URL,用于解析 HTML 中的相对路径资源

## 部署

### (建议)使用 docker compose 进行部署
Expand Down
25 changes: 18 additions & 7 deletions nonebot_plugin_htmlrender/data_source.py
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,11 @@ async def html_to_pic(
screenshot_timeout (float, optional): 截图超时时间,默认30000ms
html (str): html文本
wait (int, optional): 等待时间. Defaults to 0.
template_path (str, optional): 模板路径 如 "file:///path/to/template/"
template_path (str, optional): 模板路径,支持多种URL协议:
- file:// 本地文件路径 (如 "file:///path/to/template/")
- http:// 或 https:// 远程URL (用于远程浏览器)
- data: Data URL
- about:blank 空白页面
type (Literal["jpeg", "png"]): 图片类型, 默认 png
quality (int, optional): 图片质量 0-100 当为`png`时无效
device_scale_factor: 缩放比例,类型为float,值越大越清晰
Expand All @@ -208,8 +212,6 @@ async def html_to_pic(
bytes: 图片, 可直接发送
"""
# logger.debug(f"html:\n{html}")
if "file:" not in template_path:
raise Exception("template_path should be file:///path/to/template")
async with get_new_page(device_scale_factor, **kwargs) as page:
page.on("console", lambda msg: logger.debug(f"[Browser Console]: {msg.text}"))
await page.goto(template_path)
Expand Down Expand Up @@ -239,12 +241,16 @@ async def template_to_pic(

Args:
screenshot_timeout (float, optional): 截图超时时间,默认30000ms
template_path (str): 模板路径
template_path (str): 模板文件的本地路径 (用于jinja2加载模板)
template_name (str): 模板名
templates (Dict[Any, Any]): 模板内参数 如: {"name": "abc"}
filters (Optional[Dict[str, Any]]): 自定义过滤器
pages (Optional[Dict[Any, Any]]): 网页参数 Defaults to
{"base_url": f"file://{getcwd()}", "viewport": {"width": 500, "height": 10}}
pages (Optional[Dict[Any, Any]]): 网页参数,支持以下字段:
- base_url: 浏览器页面的基础URL,用于解析相对路径。
对于本地浏览器使用 file:// 协议,
对于远程浏览器可使用 http:// 或 https:// 协议。
默认为 f"file://{getcwd()}"
- viewport: 视口大小,默认为 {"width": 500, "height": 10}
wait (int, optional): 网页载入等待时间. Defaults to 0.
type (Literal["jpeg", "png"]): 图片类型, 默认 png
quality (int, optional): 图片质量 0-100 当为`png`时无效
Expand All @@ -270,8 +276,13 @@ async def template_to_pic(

template = template_env.get_template(template_name)

# Use base_url from pages if provided, otherwise use file:// URL from template_path
# This allows remote browsers to use http/https URLs
# while local browsers use file:// URLs
page_base_url = pages.get("base_url", f"file://{template_path}")

return await html_to_pic(
template_path=f"file://{template_path}",
template_path=page_base_url,
html=await template.render_async(**templates),
wait=wait,
type=type,
Expand Down
45 changes: 45 additions & 0 deletions tests/test_htmlrender.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,26 @@ async def test_html_to_pic(app: App, browser: None) -> None:
assert isinstance(img, bytes)


@pytest.mark.asyncio
async def test_html_to_pic_with_different_url_schemes(app: App, browser: None) -> None:
"""测试 html_to_pic 支持不同的 URL 协议"""
from nonebot_plugin_htmlrender import html_to_pic

# Test with about:blank
img = await html_to_pic(
"<html><body><p>Test with about:blank</p></body></html>",
template_path="about:blank",
)
assert isinstance(img, bytes)

# Test with data URL
img = await html_to_pic(
"<html><body><p>Test with data URL</p></body></html>",
template_path="data:text/html,<html></html>",
)
assert isinstance(img, bytes)


@pytest.mark.asyncio
@pytest.mark.parametrize("template_resources", ["text"], indirect=True)
async def test_template_to_pic(
Expand All @@ -103,6 +123,31 @@ async def test_template_to_pic(
assert isinstance(img, bytes)


@pytest.mark.asyncio
@pytest.mark.parametrize("template_resources", ["text"], indirect=True)
async def test_template_to_pic_with_different_base_url(
app: App,
browser: None,
template_resources: tuple[str, str, list[str]],
) -> None:
"""测试 template_to_pic 使用不同的 base_url"""
from nonebot_plugin_htmlrender import template_to_pic

template_path, template_name, text_list = template_resources

# Test with about:blank as base_url
img = await template_to_pic(
template_path=template_path,
template_name=template_name,
templates={"text_list": text_list},
pages={
"viewport": {"width": 600, "height": 300},
"base_url": "about:blank",
},
)
assert isinstance(img, bytes)


@pytest.mark.asyncio
async def test_template_filter(
app: App,
Expand Down
Loading