1use epub_stream::BlockRole;
2
3#[derive(Clone, Debug, Default, PartialEq)]
5pub struct RenderPage {
6 pub page_number: usize,
8 pub commands: Vec<DrawCommand>,
13 pub content_commands: Vec<DrawCommand>,
15 pub chrome_commands: Vec<DrawCommand>,
17 pub overlay_commands: Vec<DrawCommand>,
19 pub overlay_items: Vec<OverlayItem>,
21 pub annotations: Vec<PageAnnotation>,
23 pub metrics: PageMetrics,
25}
26
27impl RenderPage {
28 pub fn new(page_number: usize) -> Self {
30 Self {
31 page_number,
32 commands: Vec::with_capacity(0),
33 content_commands: Vec::with_capacity(0),
34 chrome_commands: Vec::with_capacity(0),
35 overlay_commands: Vec::with_capacity(0),
36 overlay_items: Vec::with_capacity(0),
37 annotations: Vec::with_capacity(0),
38 metrics: PageMetrics {
39 chapter_page_index: page_number.saturating_sub(1),
40 ..PageMetrics::default()
41 },
42 }
43 }
44
45 pub fn push_content_command(&mut self, cmd: DrawCommand) {
47 self.content_commands.push(cmd);
48 }
49
50 pub fn push_chrome_command(&mut self, cmd: DrawCommand) {
52 self.chrome_commands.push(cmd);
53 }
54
55 pub fn push_overlay_command(&mut self, cmd: DrawCommand) {
57 self.overlay_commands.push(cmd);
58 }
59
60 pub fn sync_commands(&mut self) {
62 #[cfg(target_os = "espidf")]
63 {
64 self.commands.clear();
67 return;
68 }
69 #[cfg(not(target_os = "espidf"))]
70 {
71 self.commands.clear();
72 self.commands.extend(self.content_commands.iter().cloned());
73 self.commands.extend(self.chrome_commands.iter().cloned());
74 self.commands.extend(self.overlay_commands.iter().cloned());
75 }
76 }
77
78 pub fn page_meta(&self) -> &PageMeta {
80 &self.metrics
81 }
82}
83
84#[derive(Clone, Debug, PartialEq, Eq)]
86pub struct PageAnnotation {
87 pub kind: String,
89 pub value: Option<String>,
91}
92
93#[derive(Clone, Copy, Debug, Default, PartialEq)]
95pub struct PageMetrics {
96 pub chapter_index: usize,
98 pub chapter_page_index: usize,
100 pub chapter_page_count: Option<usize>,
102 pub global_page_index: Option<usize>,
104 pub global_page_count_estimate: Option<usize>,
106 pub progress_chapter: f32,
108 pub progress_book: Option<f32>,
110}
111
112pub type PageMeta = PageMetrics;
114
115#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
117pub struct PaginationProfileId(pub [u8; 32]);
118
119impl PaginationProfileId {
120 pub fn from_bytes(bytes: &[u8]) -> Self {
122 fn fnv64(seed: u64, payload: &[u8]) -> u64 {
123 let mut hash = seed;
124 for b in payload {
125 hash ^= *b as u64;
126 hash = hash.wrapping_mul(0x100000001b3);
127 }
128 hash
129 }
130 let mut out = [0u8; 32];
131 let h0 = fnv64(0xcbf29ce484222325, bytes).to_le_bytes();
132 let h1 = fnv64(0x9e3779b97f4a7c15, bytes).to_le_bytes();
133 let h2 = fnv64(0xd6e8feb86659fd93, bytes).to_le_bytes();
134 let h3 = fnv64(0xa0761d6478bd642f, bytes).to_le_bytes();
135 out[0..8].copy_from_slice(&h0);
136 out[8..16].copy_from_slice(&h1);
137 out[16..24].copy_from_slice(&h2);
138 out[24..32].copy_from_slice(&h3);
139 Self(out)
140 }
141}
142
143#[derive(Clone, Debug, PartialEq)]
145pub enum OverlaySlot {
146 TopLeft,
147 TopCenter,
148 TopRight,
149 BottomLeft,
150 BottomCenter,
151 BottomRight,
152 Custom(OverlayRect),
153}
154
155#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
157pub struct OverlaySize {
158 pub width: u32,
159 pub height: u32,
160}
161
162#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
164pub struct OverlayRect {
165 pub x: i32,
166 pub y: i32,
167 pub width: u32,
168 pub height: u32,
169}
170
171#[derive(Clone, Debug, PartialEq)]
173pub enum OverlayContent {
174 Text(String),
176 Command(DrawCommand),
178}
179
180#[derive(Clone, Debug, PartialEq)]
182pub struct OverlayItem {
183 pub slot: OverlaySlot,
185 pub z: i32,
187 pub content: OverlayContent,
189}
190
191pub trait OverlayComposer {
193 fn compose(&self, metrics: &PageMetrics, viewport: OverlaySize) -> Vec<OverlayItem>;
194}
195
196#[derive(Clone, Debug, PartialEq)]
198pub enum DrawCommand {
199 Text(TextCommand),
201 Rule(RuleCommand),
203 ImageObject(ImageObjectCommand),
205 Rect(RectCommand),
207 PageChrome(PageChromeCommand),
209}
210
211#[derive(Clone, Copy, Debug, PartialEq, Eq)]
213pub struct RenderIntent {
214 pub grayscale_mode: GrayscaleMode,
216 pub dither: DitherMode,
218 pub contrast_boost: u8,
220}
221
222impl Default for RenderIntent {
223 fn default() -> Self {
224 Self {
225 grayscale_mode: GrayscaleMode::Off,
226 dither: DitherMode::None,
227 contrast_boost: 100,
228 }
229 }
230}
231
232#[derive(Clone, Copy, Debug, PartialEq, Eq)]
233pub enum GrayscaleMode {
234 Off,
235 Luminosity,
236}
237
238#[derive(Clone, Copy, Debug, PartialEq, Eq)]
239pub enum DitherMode {
240 None,
241 Ordered,
242 ErrorDiffusion,
243}
244
245#[derive(Clone, Debug, PartialEq)]
247pub struct ResolvedTextStyle {
248 pub font_id: Option<u32>,
250 pub family: String,
252 pub weight: u16,
254 pub italic: bool,
256 pub size_px: f32,
258 pub line_height: f32,
260 pub letter_spacing: f32,
262 pub role: BlockRole,
264 pub justify_mode: JustifyMode,
266}
267
268#[derive(Clone, Copy, Debug, PartialEq, Eq)]
270pub enum JustifyMode {
271 None,
273 InterWord { extra_px_total: i32 },
275 AlignRight { offset_px: i32 },
277 AlignCenter { offset_px: i32 },
279}
280
281#[derive(Clone, Debug, PartialEq)]
283pub struct TextCommand {
284 pub x: i32,
286 pub baseline_y: i32,
288 pub text: String,
290 pub font_id: Option<u32>,
292 pub style: ResolvedTextStyle,
294}
295
296#[derive(Clone, Copy, Debug, PartialEq, Eq)]
298pub struct RuleCommand {
299 pub x: i32,
301 pub y: i32,
303 pub length: u32,
305 pub thickness: u32,
307 pub horizontal: bool,
309}
310
311#[derive(Clone, Copy, Debug, PartialEq, Eq)]
313pub struct RectCommand {
314 pub x: i32,
316 pub y: i32,
318 pub width: u32,
320 pub height: u32,
322 pub fill: bool,
324}
325
326#[derive(Clone, Debug, PartialEq, Eq)]
328pub struct ImageObjectCommand {
329 pub src: String,
331 pub alt: String,
333 pub x: i32,
335 pub y: i32,
337 pub width: u32,
339 pub height: u32,
341}
342
343#[derive(Clone, Debug, PartialEq, Eq)]
345pub struct PageChromeCommand {
346 pub kind: PageChromeKind,
348 pub text: Option<String>,
350 pub current: Option<usize>,
352 pub total: Option<usize>,
354}
355
356#[derive(Clone, Copy, Debug, PartialEq, Eq)]
358pub enum PageChromeKind {
359 Header,
361 Footer,
363 Progress,
365}
366
367#[derive(Clone, Copy, Debug, PartialEq, Eq)]
369pub enum PageChromeTextStyle {
370 Regular,
371 Bold,
372 Italic,
373 BoldItalic,
374}
375
376#[derive(Clone, Copy, Debug, PartialEq, Eq)]
378pub struct PageChromeConfig {
379 pub header_enabled: bool,
381 pub footer_enabled: bool,
383 pub progress_enabled: bool,
385 pub header_x: i32,
387 pub header_baseline_y: i32,
389 pub header_style: PageChromeTextStyle,
391 pub footer_x: i32,
393 pub footer_baseline_from_bottom: i32,
395 pub footer_style: PageChromeTextStyle,
397 pub progress_x_inset: i32,
399 pub progress_y_from_bottom: i32,
401 pub progress_height: u32,
403 pub progress_stroke_width: u32,
405}
406
407impl PageChromeConfig {
408 pub const fn geometry_defaults() -> Self {
410 Self {
411 header_enabled: true,
412 footer_enabled: true,
413 progress_enabled: true,
414 header_x: 8,
415 header_baseline_y: 16,
416 header_style: PageChromeTextStyle::Bold,
417 footer_x: 8,
418 footer_baseline_from_bottom: 8,
419 footer_style: PageChromeTextStyle::Regular,
420 progress_x_inset: 8,
421 progress_y_from_bottom: 20,
422 progress_height: 4,
423 progress_stroke_width: 1,
424 }
425 }
426
427 pub const fn layout_defaults() -> Self {
429 let mut cfg = Self::geometry_defaults();
430 cfg.header_enabled = false;
431 cfg.footer_enabled = false;
432 cfg.progress_enabled = false;
433 cfg
434 }
435}
436
437impl Default for PageChromeConfig {
438 fn default() -> Self {
439 Self::layout_defaults()
440 }
441}
442
443#[derive(Clone, Copy, Debug, Default, PartialEq)]
445pub struct TypographyConfig {
446 pub hyphenation: HyphenationConfig,
448 pub widow_orphan_control: WidowOrphanControl,
450 pub justification: JustificationConfig,
452 pub hanging_punctuation: HangingPunctuationConfig,
454}
455
456#[derive(Clone, Copy, Debug, PartialEq, Eq)]
458pub struct HyphenationConfig {
459 pub soft_hyphen_policy: HyphenationMode,
461}
462
463impl Default for HyphenationConfig {
464 fn default() -> Self {
465 Self {
466 soft_hyphen_policy: HyphenationMode::Discretionary,
467 }
468 }
469}
470
471#[derive(Clone, Copy, Debug, PartialEq, Eq)]
472pub enum HyphenationMode {
473 Ignore,
474 Discretionary,
475}
476
477#[derive(Clone, Copy, Debug, PartialEq, Eq)]
479pub struct WidowOrphanControl {
480 pub min_lines: u8,
482 pub enabled: bool,
484}
485
486impl Default for WidowOrphanControl {
487 fn default() -> Self {
488 Self {
489 min_lines: 2,
490 enabled: true,
491 }
492 }
493}
494
495#[derive(Clone, Copy, Debug, PartialEq)]
497pub struct JustificationConfig {
498 pub enabled: bool,
500 pub strategy: JustificationStrategy,
502 pub min_words: usize,
504 pub min_fill_ratio: f32,
506 pub max_space_stretch_ratio: f32,
511}
512
513impl Default for JustificationConfig {
514 fn default() -> Self {
515 Self {
516 enabled: true,
517 strategy: JustificationStrategy::AdaptiveInterWord,
518 min_words: 7,
519 min_fill_ratio: 0.75,
520 max_space_stretch_ratio: 0.45,
521 }
522 }
523}
524
525#[derive(Clone, Copy, Debug, PartialEq, Eq)]
527pub enum JustificationStrategy {
528 AdaptiveInterWord,
530 FullInterWord,
532 AlignLeft,
534 AlignRight,
536 AlignCenter,
538}
539
540#[derive(Clone, Copy, Debug, PartialEq, Eq)]
542pub struct HangingPunctuationConfig {
543 pub enabled: bool,
545}
546
547impl Default for HangingPunctuationConfig {
548 fn default() -> Self {
549 Self { enabled: true }
550 }
551}
552
553#[derive(Clone, Copy, Debug, PartialEq)]
555pub struct ObjectLayoutConfig {
556 pub max_inline_image_height_ratio: f32,
558 pub cover_page_mode: CoverPageMode,
560 pub float_support: FloatSupport,
562 pub svg_mode: SvgMode,
564 pub alt_text_fallback: bool,
566}
567
568impl Default for ObjectLayoutConfig {
569 fn default() -> Self {
570 Self {
571 max_inline_image_height_ratio: 0.5,
572 cover_page_mode: CoverPageMode::Contain,
573 float_support: FloatSupport::None,
574 svg_mode: SvgMode::RasterizeFallback,
575 alt_text_fallback: true,
576 }
577 }
578}
579
580#[derive(Clone, Copy, Debug, PartialEq, Eq)]
582pub enum CoverPageMode {
583 Contain,
585 FullBleed,
587 RespectCss,
589}
590
591#[derive(Clone, Copy, Debug, PartialEq, Eq)]
592pub enum FloatSupport {
593 None,
594 Basic,
595}
596
597#[derive(Clone, Copy, Debug, PartialEq, Eq)]
598pub enum SvgMode {
599 Ignore,
600 RasterizeFallback,
601 Native,
602}