Skip to main content

nagisa_render/
theme.rs

1//! 主题与渲染配置。`RenderOptions` 是 `render_*` 的入参;`Theme` 是配色 / 字族 / 字号等
2//! 视觉口径,带亮 / 暗预设。所有尺寸是**逻辑值**,layout 前统一乘 `scale` 换设备像素。
3
4use std::collections::HashMap;
5
6use crate::font::FontHandle;
7use crate::model::Color;
8
9/// 四边内边距(逻辑像素)。
10#[derive(Clone, Copy, Debug)]
11pub struct Insets {
12    /// 上。
13    pub top: f32,
14    /// 右。
15    pub right: f32,
16    /// 下。
17    pub bottom: f32,
18    /// 左。
19    pub left: f32,
20}
21
22impl Insets {
23    /// 四边相等。
24    pub const fn all(v: f32) -> Self {
25        Self { top: v, right: v, bottom: v, left: v }
26    }
27    /// `v` = 上下,`h` = 左右。
28    pub const fn symmetric(v: f32, h: f32) -> Self {
29        Self { top: v, right: h, bottom: v, left: h }
30    }
31}
32
33/// 视觉主题:配色 + 字族 + 字号体系。预设见 [`Theme::light`] / [`Theme::dark`]。
34#[derive(Clone, Debug)]
35pub struct Theme {
36    /// 画布背景色。
37    pub background: Color,
38    /// 正文文字色。
39    pub text: Color,
40    /// 引用条 / 序号 / 链接等强调色。
41    pub accent: Color,
42    /// 图注 / 次要文字。
43    pub muted: Color,
44    /// 代码块 / 行内代码底色。
45    pub code_bg: Color,
46    /// 代码文字色。
47    pub code_text: Color,
48    /// `==高亮==` 的默认底色。
49    pub highlight: Color,
50    /// 表格 / 网格的边框线色(比 `muted` 更淡)。
51    pub border: Color,
52    /// 无衬线字族名。
53    pub font_sans: String,
54    /// 衬线字族名。
55    pub font_serif: String,
56    /// 等宽字族名。
57    pub font_mono: String,
58    /// 楷体字族名。
59    pub font_kai: String,
60    /// 基准字号(逻辑像素)。
61    pub base_size: f32,
62    /// 行高倍率。
63    pub line_height: f32,
64    /// h1..h6 相对基准字号的倍率。
65    pub heading_scale: [f32; 6],
66}
67
68impl Theme {
69    /// 亮色预设。
70    pub fn light() -> Self {
71        Self {
72            background: Color::rgb(0xff, 0xff, 0xff),
73            text: Color::rgb(0x1f, 0x23, 0x28),
74            accent: Color::rgb(0x25, 0x63, 0xeb),
75            muted: Color::rgb(0x6e, 0x77, 0x81),
76            code_bg: Color::rgb(0xf3, 0xf4, 0xf6),
77            code_text: Color::rgb(0x1f, 0x23, 0x28),
78            highlight: Color::rgb(0xff, 0xf1, 0xa8),
79            border: Color::rgb(0xe5, 0xe7, 0xeb),
80            ..Self::common()
81        }
82    }
83
84    /// 暗色预设。
85    pub fn dark() -> Self {
86        Self {
87            background: Color::rgb(0x0d, 0x11, 0x17),
88            text: Color::rgb(0xe6, 0xed, 0xf3),
89            accent: Color::rgb(0x58, 0xa6, 0xff),
90            muted: Color::rgb(0x8b, 0x94, 0x9e),
91            code_bg: Color::rgb(0x16, 0x1b, 0x22),
92            code_text: Color::rgb(0xe6, 0xed, 0xf3),
93            highlight: Color::rgb(0x57, 0x4a, 0x1a),
94            border: Color::rgb(0x30, 0x36, 0x3d),
95            ..Self::common()
96        }
97    }
98
99    /// 亮 / 暗共享的非配色部分(字族 / 字号 / 行高 / 标题阶梯)。字族名对应内置字体。
100    fn common() -> Self {
101        Self {
102            background: Color::rgb(0, 0, 0),
103            text: Color::rgb(0, 0, 0),
104            accent: Color::rgb(0, 0, 0),
105            muted: Color::rgb(0, 0, 0),
106            code_bg: Color::rgb(0, 0, 0),
107            code_text: Color::rgb(0, 0, 0),
108            highlight: Color::rgb(0, 0, 0),
109            border: Color::rgb(0, 0, 0),
110            font_sans: "Noto Sans SC".to_string(), // 内置
111            font_serif: "Noto Serif SC".to_string(), // 内置(思源宋体)
112            font_mono: "JetBrains Mono".to_string(), // 内置(CJK 在等宽语境回退 Noto)
113            font_kai: "LXGW WenKai GB".to_string(),  // 内置(霞鹜文楷)
114            base_size: 30.0,
115            line_height: 1.5,
116            heading_scale: [2.0, 1.6, 1.35, 1.15, 1.0, 0.9],
117        }
118    }
119}
120
121impl Default for Theme {
122    fn default() -> Self {
123        Self::light()
124    }
125}
126
127/// 输出图片格式。文字图首选 `Webp`(最小 + 快);`Png` 通用兜底;`PngFast` 要 PNG 又要快。
128#[derive(Clone, Copy, Debug, PartialEq, Eq)]
129pub enum OutputFormat {
130    /// PNG(无损,平衡压缩,默认——通用兼容)。
131    Png,
132    /// PNG(无损,快压缩:约 8 倍快、体积大 ~40%)。必须出 PNG 又要快时用。
133    PngFast,
134    /// WebP(无损;通常体积最小、速度也好)。文字图首选;画布单边 > 16383px(WebP 上限)时编码报错。
135    Webp,
136    /// WebP 优先,画布单边 > 16383px 时自动落 PNG。要 WebP 的体积、又不想为超长图单独处理报错时用
137    /// ——超限会**改格式**,显式选了才发生。
138    WebpOrPng,
139}
140
141/// 渲染入参。链式覆写;`default()` = 720 逻辑宽、亮色、scale 2、PNG、默认字体句柄。
142#[derive(Clone)]
143pub struct RenderOptions {
144    /// 逻辑内容宽(含左右内边距),默认 720。
145    pub width: f32,
146    /// 页边距(逻辑像素)。
147    pub padding: Insets,
148    /// 超采样系数(输出 = 逻辑尺寸 × scale),默认 2.0。越大越清晰也越慢 / 越大。
149    pub scale: f32,
150    /// 视觉主题。
151    pub theme: Theme,
152    /// 字体栈句柄。
153    pub fonts: FontHandle,
154    /// 输出格式,默认 PNG。
155    pub format: OutputFormat,
156    /// 标记文本里 `@名字` 图片 → 字节。
157    pub images: HashMap<String, Vec<u8>>,
158}
159
160impl Default for RenderOptions {
161    fn default() -> Self {
162        Self {
163            width: 720.0,
164            padding: Insets::symmetric(32.0, 40.0),
165            scale: 2.0,
166            theme: Theme::light(),
167            fonts: FontHandle::shared_default(),
168            format: OutputFormat::Png,
169            images: HashMap::new(),
170        }
171    }
172}
173
174impl RenderOptions {
175    /// 设逻辑内容宽。
176    pub fn with_width(mut self, w: f32) -> Self {
177        self.width = w;
178        self
179    }
180    /// 设页边距(逻辑像素)。
181    pub fn with_padding(mut self, p: Insets) -> Self {
182        self.padding = p;
183        self
184    }
185    /// 设主题。
186    pub fn with_theme(mut self, t: Theme) -> Self {
187        self.theme = t;
188        self
189    }
190    /// 设字体句柄。
191    pub fn with_fonts(mut self, f: FontHandle) -> Self {
192        self.fonts = f;
193        self
194    }
195    /// 设超采样系数(清晰度档位,见 `fast`/`sharp`/`ultra` 预设)。
196    pub fn with_scale(mut self, s: f32) -> Self {
197        self.scale = s.clamp(0.25, 8.0);
198        self
199    }
200    /// 清晰度预设:快(scale 1)——最省、体积小,清晰度一般。
201    pub fn fast(self) -> Self {
202        self.with_scale(1.0)
203    }
204    /// 清晰度预设:标准(scale 1.5)。
205    pub fn standard(self) -> Self {
206        self.with_scale(1.5)
207    }
208    /// 清晰度预设:清晰(scale 2,默认)。
209    pub fn sharp(self) -> Self {
210        self.with_scale(2.0)
211    }
212    /// 清晰度预设:超清(scale 3)——最清晰也最慢 / 最大。
213    pub fn ultra(self) -> Self {
214        self.with_scale(3.0)
215    }
216    /// 设输出格式。
217    pub fn with_format(mut self, f: OutputFormat) -> Self {
218        self.format = f;
219        self
220    }
221    /// 输出 PNG(无损,平衡压缩)。
222    pub fn png(self) -> Self {
223        self.with_format(OutputFormat::Png)
224    }
225    /// 输出 PNG(无损,快压缩——更快但更大)。
226    pub fn png_fast(self) -> Self {
227        self.with_format(OutputFormat::PngFast)
228    }
229    /// 输出 WebP(无损,文字图首选)。画布单边 > 16383px 时编码报错。
230    pub fn webp(self) -> Self {
231        self.with_format(OutputFormat::Webp)
232    }
233    /// 输出 WebP,但画布单边 > 16383px(WebP 上限)时自动落 PNG。
234    pub fn webp_or_png(self) -> Self {
235        self.with_format(OutputFormat::WebpOrPng)
236    }
237}