nagisa_render/lib.rs
1//! 把一段带格式的文本 / 一份用构建器拼出的文档,排版渲染成图片字节。
2//!
3//! 给的是图片字节,不碰任何协议——送到 QQ 由调用方包一层(如 `Segment::image_bytes`)。
4//! 管线:源(标记文本 | 构建器)→ 文档模型 [`Document`] → 版式(cosmic-text 整形 / 断行 /
5//! 字体回退 / CJK+拉丁+emoji 混排)→ 光栅(tiny-skia)→ 图片字节。
6//!
7//! # 两种写法
8//!
9//! 标记文本(类 Markdown,适合「一大段文字」):
10//!
11//! ```ignore
12//! use nagisa_render::{render_markup, RenderOptions};
13//!
14//! let png = render_markup("# 标题\n\n正文 **加粗**、[彩色]{color=#e00}、==高亮==。", &RenderOptions::default())?;
15//! ```
16//!
17//! Rust 构建器(类型安全,适合从数据生成卡片):
18//!
19//! ```ignore
20//! use nagisa_render::{render_document, Doc, RenderOptions};
21//!
22//! let doc = Doc::new()
23//! .heading(1, |h| h.text("月度报告"))
24//! .paragraph(|p| { p.text("环比 ").bold("+12%").text("。"); })
25//! .table(|t| { t.head(["项", "值"]).row(["发言", "3450"]); })
26//! .build();
27//! let png = render_document(&doc, &RenderOptions::default())?;
28//! ```
29//!
30//! 两种写法产出同一个 [`Document`];也可以 [`parse_markup`] 拿到 `Document` 再用构建器接着改。
31//!
32//! # 能排什么
33//!
34//! 标题、段落、粗 / 细 / 任意字重、斜 / 下划 / 删除、颜色 / 高亮、字号 / 字族(黑 / 宋 /
35//! 楷 / 等宽)、文字阴影、链接(图片点不了,取文字按强调色渲染)、行内与块级代码(语言标签渲染在盒角,
36//! 块级带轻量语法上色:rust / json / toml / python / js / shell / c 系,四类词色随主题
37//! [`CodePalette`],认不出的语言整块默认色)、有序 / 无序列表(可嵌套)、任务列表(`- [ ]` / `- [x]` → `□` / `✓`)、引用、
38//! 分割线、图片(缩放 / 对齐 / 图注 + 装饰层:角标 / 边框 / 水印 / 圆角裁切 / 投影,见
39//! [`ImageBuilder`])、左中右两端对齐、多栏并排([`Columns`])、面板([`Panel`]:底色 / 边框 /
40//! 圆角 / 内边距 / 投影的卡片容器,作并排栏整栏时自动拉齐行高;`::: panel {bg=…}` /
41//! [`Doc::panel`])、表格([`Table`]:自适应列宽 / 限宽 / 铺满可用宽(`expand`)/ 按列行格
42//! 上色 / 紧凑度与网格可调)。标记语言对应是 Markdown 基底加少量扩展(`==高亮==`、
43//! `[文字]{属性}`、`::: 围栏`、GFM 表格),见 [`parse_markup`];构建器见 [`Doc`]。
44//!
45//! # 输出与配置
46//!
47//! 都在 [`RenderOptions`]:
48//!
49//! - **格式** [`OutputFormat`]:`Png`(默认,通用)/ `PngFast`(更快、略大)/ `Webp`(无损,
50//! 文字图体积最小且快;单边 > 16383px 报错)/ `WebpOrPng`(WebP 优先,超 WebP 上限自动落 PNG)。
51//! 文字图建议 `.webp()`,长图怕超限用 `.webp_or_png()`。
52//! - **清晰度** `scale`:`.fast()` / `.standard()` / `.sharp()`(默认 2×)/ `.ultra()`,或
53//! `.with_scale(f)`。越大越清晰、也越慢越大。
54//! - **主题** [`Theme`]:亮 / 暗预设 + 自定义配色 / 字族 / 字号。
55//! - **字体** [`FontHandle`]:内置兜底(黑体 + 等宽正斜,细 / 常规 / 粗皆真字形,zstd 压缩
56//! 内嵌、首次使用时解压)+ 自备数据(`data()`,裸字节或 zstd 压缩皆可)+ 自定义目录 +
57//! 系统,四来源合并。衬线 / 楷体角色不随包内置(默认字族名 Noto Serif SC / LXGW WenKai GB,
58//! 自备对应字体即生效),缺字体时回退黑体。
59//! - **页眉 / 页脚** [`PageChrome`]:与文档无关的固定标识(品牌 / 署名 / 出处),配在选项上
60//! 所有出图统一带;富文本、左右分栏(`trailing`)、满幅色带(`band`)皆可。
61//!
62//! 入口:[`render_markup`] / [`render_document`](→ 图片字节)、[`render_to_rgba`](→ RGBA 图,
63//! 供进一步合成)、[`measure_document`](只排版量尺寸不绘制,按高度上限把长内容切成多张图
64//! 时先量再渲)。
65#![forbid(unsafe_code)]
66
67mod build;
68mod error;
69mod font;
70mod highlight;
71mod layout;
72mod markup;
73mod model;
74mod paint;
75mod theme;
76
77pub use build::{
78 BadgeBuilder, ColumnsBuilder, Doc, ImageBuilder, ListBuilder, PanelBuilder, ParaBuilder,
79 ProgressBuilder, StyleBuilder, TableBuilder, WatermarkBuilder,
80};
81pub use error::{Error, Result};
82pub use font::FontHandle;
83pub use markup::parse as parse_markup;
84pub use model::ImageSource;
85pub use model::{
86 Align, Anchor, Badge, Block, BlockImage, Cell, ColSpec, Color, Column, Columns, Document,
87 DotMark, FontRole, Highlight, ImageBorder, ImageDecor, Inline, Length, List, ListItem,
88 ListKind, Panel, PanelDecor, Progress, RingMark, Shadow, Table, TableGrid, TableStyle,
89 TextStyle, Watermark,
90};
91pub use theme::{CodePalette, Insets, OutputFormat, PageChrome, RenderOptions, Theme};
92
93/// 解析标记文本并渲染成图片字节(格式由 [`RenderOptions::format`] 决定,默认 PNG)。
94pub fn render_markup(src: &str, opts: &RenderOptions) -> Result<Vec<u8>> {
95 render_document(&markup::parse(src)?, opts)
96}
97
98/// 把一份文档排版渲染成图片字节(格式见 [`RenderOptions::format`])。
99pub fn render_document(doc: &Document, opts: &RenderOptions) -> Result<Vec<u8>> {
100 let layout = layout::layout_document(doc, opts)?;
101 paint::paint(&layout, opts)
102}
103
104/// 渲染成(去预乘的)RGBA 图,供进一步合成,不编码。
105pub fn render_to_rgba(doc: &Document, opts: &RenderOptions) -> Result<image::RgbaImage> {
106 let layout = layout::layout_document(doc, opts)?;
107 paint::paint_rgba(&layout, opts)
108}
109
110/// 只排版、不绘制:返回这份文档渲出来的图片尺寸(**物理像素**,即已含 `scale`)。
111/// 供调用方做内容装箱——如按高度上限把长列表切成多张图,定好切分再真正渲染。
112pub fn measure_document(doc: &Document, opts: &RenderOptions) -> Result<(u32, u32)> {
113 let layout = layout::layout_document(doc, opts)?;
114 Ok((layout.width_px, layout.height_px))
115}