1use std::path::PathBuf;
17
18use crate::model::{
19 Align, Anchor, Badge, Block, BlockImage, Cell, ColSpec, Color, Column, Columns, Document,
20 FontRole, Highlight, ImageBorder, ImageDecor, Inline, Length, List, ListItem, ListKind,
21 Panel, PanelDecor, Progress, Shadow, Table, TableStyle, TextStyle, Watermark,
22};
23
24#[derive(Default)]
26pub struct Doc {
27 blocks: Vec<Block>,
28}
29
30impl Doc {
31 pub fn new() -> Self {
33 Self { blocks: Vec::new() }
34 }
35
36 pub fn build(&self) -> Document {
38 Document { blocks: self.blocks.clone() }
39 }
40
41 pub fn heading<R>(&mut self, level: u8, f: impl FnOnce(&mut ParaBuilder) -> R) -> &mut Self {
43 let mut pb = ParaBuilder::new();
44 let _ = f(&mut pb);
45 self.blocks.push(Block::Heading {
46 level: level.clamp(1, 6),
47 inlines: pb.inlines,
48 align: pb.align,
49 });
50 self
51 }
52
53 pub fn paragraph<R>(&mut self, f: impl FnOnce(&mut ParaBuilder) -> R) -> &mut Self {
55 let mut pb = ParaBuilder::new();
56 let _ = f(&mut pb);
57 self.blocks.push(Block::Paragraph { inlines: pb.inlines, align: pb.align });
58 self
59 }
60
61 pub fn text(&mut self, s: impl Into<String>) -> &mut Self {
63 self.paragraph(|p| {
64 p.text(s);
65 })
66 }
67
68 pub fn quote<R>(&mut self, f: impl FnOnce(&mut Doc) -> R) -> &mut Self {
70 let mut inner = Doc::new();
71 let _ = f(&mut inner);
72 self.blocks.push(Block::Quote(inner.blocks));
73 self
74 }
75
76 pub fn list<R>(&mut self, kind: ListKind, f: impl FnOnce(&mut ListBuilder) -> R) -> &mut Self {
78 let mut lb = ListBuilder { kind, start: 1, items: Vec::new() };
79 let _ = f(&mut lb);
80 self.blocks.push(Block::List(List { kind: lb.kind, start: lb.start, items: lb.items }));
81 self
82 }
83
84 pub fn code(&mut self, lang: impl Into<String>, text: impl Into<String>) -> &mut Self {
86 let lang = lang.into();
87 self.blocks.push(Block::Code {
88 lang: if lang.is_empty() { None } else { Some(lang) },
89 text: text.into(),
90 });
91 self
92 }
93
94 pub fn divider(&mut self) -> &mut Self {
96 self.blocks.push(Block::Divider);
97 self
98 }
99
100 pub fn panel<R>(&mut self, f: impl FnOnce(&mut PanelBuilder) -> R) -> &mut Self {
104 let mut pb = PanelBuilder::new();
105 let _ = f(&mut pb);
106 self.blocks.push(Block::Panel(pb.into_panel()));
107 self
108 }
109
110 pub fn columns<R>(&mut self, f: impl FnOnce(&mut ColumnsBuilder) -> R) -> &mut Self {
112 let mut cb = ColumnsBuilder { gap: None, cols: Vec::new() };
113 let _ = f(&mut cb);
114 self.blocks.push(Block::Columns(Columns { cols: cb.cols, gap: cb.gap }));
115 self
116 }
117
118 pub fn table<R>(&mut self, f: impl FnOnce(&mut TableBuilder) -> R) -> &mut Self {
120 let mut tb = TableBuilder {
121 header: None,
122 rows: Vec::new(),
123 cols: Vec::new(),
124 style: TableStyle::default(),
125 };
126 let _ = f(&mut tb);
127 self.blocks.push(Block::Table(Table {
128 header: tb.header,
129 rows: tb.rows,
130 cols: tb.cols,
131 style: tb.style,
132 }));
133 self
134 }
135
136 pub fn progress<R>(&mut self, value: f32, f: impl FnOnce(&mut ProgressBuilder) -> R) -> &mut Self {
140 let mut pb = ProgressBuilder {
141 p: Progress {
142 value,
143 height: 10.0,
144 fill: None,
145 track: None,
146 radius: None,
147 width: None,
148 align: Align::Left,
149 },
150 };
151 let _ = f(&mut pb);
152 self.blocks.push(Block::Progress(pb.p));
153 self
154 }
155
156 pub fn image_bytes<R>(
158 &mut self,
159 bytes: Vec<u8>,
160 f: impl FnOnce(&mut ImageBuilder) -> R,
161 ) -> &mut Self {
162 self.push_block_image(ImageSource::Bytes(bytes), f)
163 }
164
165 pub fn image_path<R>(
167 &mut self,
168 path: impl Into<PathBuf>,
169 f: impl FnOnce(&mut ImageBuilder) -> R,
170 ) -> &mut Self {
171 self.push_block_image(ImageSource::Path(path.into()), f)
172 }
173
174 fn push_block_image<R>(
175 &mut self,
176 src: ImageSource,
177 f: impl FnOnce(&mut ImageBuilder) -> R,
178 ) -> &mut Self {
179 let mut ib = ImageBuilder {
180 width: None,
181 align: Align::Left,
182 caption: None,
183 decor: ImageDecor::default(),
184 };
185 let _ = f(&mut ib);
186 self.blocks.push(Block::Image(BlockImage {
187 src,
188 width: ib.width,
189 align: ib.align,
190 caption: ib.caption,
191 decor: ib.decor,
192 }));
193 self
194 }
195}
196
197use crate::model::ImageSource;
198
199pub struct ParaBuilder {
201 inlines: Vec<Inline>,
202 align: Align,
203}
204
205impl ParaBuilder {
206 pub(crate) fn new() -> Self {
207 Self { inlines: Vec::new(), align: Align::Left }
208 }
209
210 pub(crate) fn into_inlines(self) -> Vec<Inline> {
212 self.inlines
213 }
214
215 pub fn align(&mut self, a: Align) -> &mut Self {
217 self.align = a;
218 self
219 }
220
221 pub fn text(&mut self, s: impl Into<String>) -> &mut Self {
223 self.push(s, TextStyle::default())
224 }
225
226 pub fn bold(&mut self, s: impl Into<String>) -> &mut Self {
228 self.push(s, TextStyle { weight: Some(700), ..Default::default() })
229 }
230
231 pub fn light(&mut self, s: impl Into<String>) -> &mut Self {
233 self.push(s, TextStyle { weight: Some(300), ..Default::default() })
234 }
235
236 pub fn italic(&mut self, s: impl Into<String>) -> &mut Self {
238 self.push(s, TextStyle { italic: true, ..Default::default() })
239 }
240
241 pub fn underline(&mut self, s: impl Into<String>) -> &mut Self {
243 self.push(s, TextStyle { underline: true, ..Default::default() })
244 }
245
246 pub fn strike(&mut self, s: impl Into<String>) -> &mut Self {
248 self.push(s, TextStyle { strike: true, ..Default::default() })
249 }
250
251 pub fn highlight(&mut self, s: impl Into<String>) -> &mut Self {
253 self.push(s, TextStyle { highlight: Some(Highlight::Theme), ..Default::default() })
254 }
255
256 pub fn color(&mut self, hex: &str, s: impl Into<String>) -> &mut Self {
258 self.push(s, TextStyle { color: Color::hex(hex), ..Default::default() })
259 }
260
261 pub fn code(&mut self, s: impl Into<String>) -> &mut Self {
263 self.inlines.push(Inline::Code(s.into()));
264 self
265 }
266
267 pub fn styled<R>(
269 &mut self,
270 s: impl Into<String>,
271 f: impl FnOnce(&mut StyleBuilder) -> R,
272 ) -> &mut Self {
273 let mut sb = StyleBuilder { style: TextStyle::default() };
274 let _ = f(&mut sb);
275 self.push(s, sb.style)
276 }
277
278 pub fn line_break(&mut self) -> &mut Self {
280 self.inlines.push(Inline::LineBreak);
281 self
282 }
283
284 fn push(&mut self, s: impl Into<String>, style: TextStyle) -> &mut Self {
285 self.inlines.push(Inline::Text { text: s.into(), style });
286 self
287 }
288}
289
290pub struct StyleBuilder {
292 style: TextStyle,
293}
294
295impl StyleBuilder {
296 pub fn bold(&mut self) -> &mut Self {
298 self.style.weight = Some(700);
299 self
300 }
301 pub fn light(&mut self) -> &mut Self {
303 self.style.weight = Some(300);
304 self
305 }
306 pub fn weight(&mut self, w: u16) -> &mut Self {
308 if (1..=1000).contains(&w) {
309 self.style.weight = Some(w);
310 }
311 self
312 }
313 pub fn italic(&mut self) -> &mut Self {
315 self.style.italic = true;
316 self
317 }
318 pub fn underline(&mut self) -> &mut Self {
320 self.style.underline = true;
321 self
322 }
323 pub fn strike(&mut self) -> &mut Self {
325 self.style.strike = true;
326 self
327 }
328 pub fn color(&mut self, hex: &str) -> &mut Self {
330 if let Some(c) = Color::hex(hex) {
331 self.style.color = Some(c);
332 }
333 self
334 }
335 pub fn bg(&mut self, hex: &str) -> &mut Self {
337 if let Some(c) = Color::hex(hex) {
338 self.style.highlight = Some(Highlight::Custom(c));
339 }
340 self
341 }
342 pub fn size(&mut self, mult: f32) -> &mut Self {
345 if mult.is_finite() && mult > 0.0 {
346 self.style.size = mult;
347 }
348 self
349 }
350 pub fn font(&mut self, role: FontRole) -> &mut Self {
352 self.style.font = role;
353 self
354 }
355 pub fn ring(&mut self) -> &mut Self {
359 self.style.ring.get_or_insert_default();
360 self
361 }
362 pub fn ring_color(&mut self, hex: &str) -> &mut Self {
364 let r = self.style.ring.get_or_insert_default();
365 r.color = Color::hex(hex).or(r.color);
366 self
367 }
368 pub fn ring_radius(&mut self, r: f32) -> &mut Self {
370 self.ring_radii(r, r)
371 }
372 pub fn ring_radii(&mut self, rx: f32, ry: f32) -> &mut Self {
374 let r = self.style.ring.get_or_insert_default();
375 if rx.is_finite() && rx > 0.0 {
376 r.rx = Some(rx);
377 }
378 if ry.is_finite() && ry > 0.0 {
379 r.ry = Some(ry);
380 }
381 self
382 }
383 pub fn ring_stroke(&mut self, w: f32) -> &mut Self {
385 let r = self.style.ring.get_or_insert_default();
386 if w.is_finite() && w > 0.0 {
387 r.width = Some(w);
388 }
389 self
390 }
391 pub fn ring_each(&mut self) -> &mut Self {
394 self.style.ring.get_or_insert_default().each = true;
395 self
396 }
397 pub fn dot(&mut self) -> &mut Self {
399 self.style.dot.get_or_insert_default();
400 self
401 }
402 pub fn dot_color(&mut self, hex: &str) -> &mut Self {
404 let d = self.style.dot.get_or_insert_default();
405 d.color = Color::hex(hex).or(d.color);
406 self
407 }
408 pub fn dot_radius(&mut self, r: f32) -> &mut Self {
410 let d = self.style.dot.get_or_insert_default();
411 if r.is_finite() && r > 0.0 {
412 d.radius = Some(r);
413 }
414 self
415 }
416 pub fn dot_each(&mut self) -> &mut Self {
418 self.style.dot.get_or_insert_default().each = true;
419 self
420 }
421 pub fn aside_right(&mut self) -> &mut Self {
425 self.style.aside = Some(crate::model::AsideSide::Right);
426 self
427 }
428 pub fn aside_left(&mut self) -> &mut Self {
430 self.style.aside = Some(crate::model::AsideSide::Left);
431 self
432 }
433 pub fn shadow(&mut self) -> &mut Self {
434 self.style.shadow = Some(Shadow::default());
435 self
436 }
437 pub fn shadow_with(&mut self, dx: f32, dy: f32, blur: f32, hex: &str) -> &mut Self {
439 if let Some(color) = Color::hex(hex) {
440 self.style.shadow = Some(Shadow { dx, dy, blur: blur.max(0.0), color });
441 }
442 self
443 }
444}
445
446pub struct TableBuilder {
448 header: Option<Vec<Cell>>,
449 rows: Vec<Vec<Cell>>,
450 cols: Vec<ColSpec>,
451 style: TableStyle,
452}
453
454impl TableBuilder {
455 pub fn head<I, S>(&mut self, cells: I) -> &mut Self
457 where
458 I: IntoIterator<Item = S>,
459 S: Into<String>,
460 {
461 self.header = Some(cells.into_iter().map(text_cell).collect());
462 self
463 }
464 pub fn row<I, S>(&mut self, cells: I) -> &mut Self
466 where
467 I: IntoIterator<Item = S>,
468 S: Into<String>,
469 {
470 self.rows.push(cells.into_iter().map(text_cell).collect());
471 self
472 }
473 pub fn align<I: IntoIterator<Item = Align>>(&mut self, aligns: I) -> &mut Self {
475 for (k, a) in aligns.into_iter().enumerate() {
476 self.ensure_col(k).align = a;
477 }
478 self
479 }
480 pub fn width(&mut self, col: usize, w: Length) -> &mut Self {
482 self.ensure_col(col).width = Some(w);
483 self
484 }
485 fn ensure_col(&mut self, k: usize) -> &mut ColSpec {
486 while self.cols.len() <= k {
487 self.cols.push(ColSpec::default());
488 }
489 &mut self.cols[k]
490 }
491
492 pub fn col_style<R>(&mut self, col: usize, f: impl Fn(&mut StyleBuilder) -> R) -> &mut Self {
496 if let Some(h) = self.header.as_mut().and_then(|h| h.get_mut(col)) {
497 style_cell(h, &f);
498 }
499 for row in &mut self.rows {
500 if let Some(c) = row.get_mut(col) {
501 style_cell(c, &f);
502 }
503 }
504 self
505 }
506 pub fn row_style<R>(&mut self, row: usize, f: impl Fn(&mut StyleBuilder) -> R) -> &mut Self {
508 if let Some(r) = self.rows.get_mut(row) {
509 for c in r.iter_mut() {
510 style_cell(c, &f);
511 }
512 }
513 self
514 }
515 pub fn cell_style<R>(
517 &mut self,
518 row: usize,
519 col: usize,
520 f: impl Fn(&mut StyleBuilder) -> R,
521 ) -> &mut Self {
522 if let Some(c) = self.rows.get_mut(row).and_then(|r| r.get_mut(col)) {
523 style_cell(c, &f);
524 }
525 self
526 }
527 pub fn col_fill(&mut self, col: usize, hex: &str) -> &mut Self {
529 let bg = Color::hex(hex);
530 if let Some(h) = self.header.as_mut().and_then(|h| h.get_mut(col)) {
531 h.bg = bg;
532 }
533 for row in &mut self.rows {
534 if let Some(c) = row.get_mut(col) {
535 c.bg = bg;
536 }
537 }
538 self
539 }
540 pub fn row_fill(&mut self, row: usize, hex: &str) -> &mut Self {
542 let bg = Color::hex(hex);
543 if let Some(r) = self.rows.get_mut(row) {
544 for c in r.iter_mut() {
545 c.bg = bg;
546 }
547 }
548 self
549 }
550 pub fn cell_fill(&mut self, row: usize, col: usize, hex: &str) -> &mut Self {
552 if let Some(c) = self.rows.get_mut(row).and_then(|r| r.get_mut(col)) {
553 c.bg = Color::hex(hex);
554 }
555 self
556 }
557
558 pub fn pad_x(&mut self, px: f32) -> &mut Self {
562 self.style.pad_x = Some(px.max(0.0));
563 self
564 }
565 pub fn pad_y(&mut self, px: f32) -> &mut Self {
567 self.style.pad_y = Some(px.max(0.0));
568 self
569 }
570 pub fn expand(&mut self) -> &mut Self {
572 self.style.expand = true;
573 self
574 }
575 pub fn grid_outer(&mut self, on: bool) -> &mut Self {
577 self.style.grid.outer = on;
578 self
579 }
580 pub fn grid_vertical(&mut self, on: bool) -> &mut Self {
582 self.style.grid.vertical = on;
583 self
584 }
585 pub fn grid_horizontal(&mut self, on: bool) -> &mut Self {
587 self.style.grid.horizontal = on;
588 self
589 }
590 pub fn no_grid(&mut self) -> &mut Self {
592 self.style.grid.outer = false;
593 self.style.grid.vertical = false;
594 self.style.grid.horizontal = false;
595 self
596 }
597 pub fn header_fill(&mut self, on: bool) -> &mut Self {
599 self.style.header_fill = on;
600 self
601 }
602}
603
604fn text_cell(s: impl Into<String>) -> Cell {
606 Cell { inlines: vec![Inline::Text { text: s.into(), style: TextStyle::default() }], bg: None }
607}
608
609fn style_cell<R>(cell: &mut Cell, f: &impl Fn(&mut StyleBuilder) -> R) {
611 for inl in &mut cell.inlines {
612 if let Inline::Text { style, .. } = inl {
613 let mut sb = StyleBuilder { style: style.clone() };
614 let _ = f(&mut sb);
615 *style = sb.style;
616 }
617 }
618}
619
620pub struct ColumnsBuilder {
622 gap: Option<f32>,
623 cols: Vec<Column>,
624}
625
626impl ColumnsBuilder {
627 pub fn gap(&mut self, g: f32) -> &mut Self {
629 self.gap = Some(g);
630 self
631 }
632 pub fn col<R>(&mut self, f: impl FnOnce(&mut Doc) -> R) -> &mut Self {
634 self.col_weighted(1.0, f)
635 }
636 pub fn col_weighted<R>(&mut self, weight: f32, f: impl FnOnce(&mut Doc) -> R) -> &mut Self {
638 let mut inner = Doc::new();
639 let _ = f(&mut inner);
640 self.cols.push(Column { blocks: inner.blocks, weight });
641 self
642 }
643 pub fn panel<R>(&mut self, f: impl FnOnce(&mut PanelBuilder) -> R) -> &mut Self {
645 self.panel_weighted(1.0, f)
646 }
647 pub fn panel_weighted<R>(
649 &mut self,
650 weight: f32,
651 f: impl FnOnce(&mut PanelBuilder) -> R,
652 ) -> &mut Self {
653 let mut pb = PanelBuilder::new();
654 let _ = f(&mut pb);
655 self.cols.push(Column { blocks: vec![Block::Panel(pb.into_panel())], weight });
656 self
657 }
658}
659
660pub struct PanelBuilder {
664 doc: Doc,
665 decor: PanelDecor,
666}
667
668impl PanelBuilder {
669 fn new() -> Self {
670 Self { doc: Doc::new(), decor: PanelDecor::default() }
671 }
672
673 fn into_panel(self) -> Panel {
674 Panel { blocks: self.doc.blocks, decor: self.decor }
675 }
676
677 pub fn bg(&mut self, hex: &str) -> &mut Self {
679 if let Some(c) = Color::hex(hex) {
680 self.decor.bg = Some(c);
681 }
682 self
683 }
684 pub fn border(&mut self, width: f32, hex: &str) -> &mut Self {
686 if width > 0.0 && width.is_finite() {
687 if let Some(color) = Color::hex(hex) {
688 self.decor.border = Some(ImageBorder { width, color });
689 }
690 }
691 self
692 }
693 pub fn rounded(&mut self, radius: f32) -> &mut Self {
695 if radius.is_finite() && radius >= 0.0 {
696 self.decor.radius = Some(radius);
697 }
698 self
699 }
700 pub fn pad(&mut self, px: f32) -> &mut Self {
702 if px.is_finite() && px >= 0.0 {
703 self.decor.pad = Some(px);
704 }
705 self
706 }
707 pub fn shadow(&mut self) -> &mut Self {
709 self.decor.shadow = Some(Shadow::default());
710 self
711 }
712 pub fn shadow_with(&mut self, dx: f32, dy: f32, blur: f32, hex: &str) -> &mut Self {
714 if let Some(color) = Color::hex(hex) {
715 self.decor.shadow = Some(Shadow { dx, dy, blur: blur.max(0.0), color });
716 }
717 self
718 }
719}
720
721impl std::ops::Deref for PanelBuilder {
722 type Target = Doc;
723 fn deref(&self) -> &Doc {
724 &self.doc
725 }
726}
727
728impl std::ops::DerefMut for PanelBuilder {
729 fn deref_mut(&mut self) -> &mut Doc {
730 &mut self.doc
731 }
732}
733
734pub struct ProgressBuilder {
736 p: Progress,
737}
738
739impl ProgressBuilder {
740 pub fn height(&mut self, h: f32) -> &mut Self {
742 self.p.height = h;
743 self
744 }
745 pub fn fill(&mut self, hex: &str) -> &mut Self {
747 self.p.fill = Color::hex(hex).or(self.p.fill);
748 self
749 }
750 pub fn track(&mut self, hex: &str) -> &mut Self {
752 self.p.track = Color::hex(hex).or(self.p.track);
753 self
754 }
755 pub fn radius(&mut self, r: f32) -> &mut Self {
757 self.p.radius = Some(r);
758 self
759 }
760 pub fn width_px(&mut self, px: f32) -> &mut Self {
762 self.p.width = Some(Length::Px(px));
763 self
764 }
765 pub fn width_percent(&mut self, pct: f32) -> &mut Self {
767 self.p.width = Some(Length::Percent(pct));
768 self
769 }
770 pub fn align(&mut self, a: Align) -> &mut Self {
772 self.p.align = a;
773 self
774 }
775}
776
777pub struct ListBuilder {
779 kind: ListKind,
780 start: u32,
781 items: Vec<ListItem>,
782}
783
784impl ListBuilder {
785 pub fn start(&mut self, n: u32) -> &mut Self {
787 self.start = n;
788 self
789 }
790 pub fn item<R>(&mut self, f: impl FnOnce(&mut Doc) -> R) -> &mut Self {
792 let mut inner = Doc::new();
793 let _ = f(&mut inner);
794 self.items.push(ListItem { blocks: inner.blocks, check: None });
795 self
796 }
797 pub fn task<R>(&mut self, done: bool, f: impl FnOnce(&mut Doc) -> R) -> &mut Self {
799 let mut inner = Doc::new();
800 let _ = f(&mut inner);
801 self.items.push(ListItem { blocks: inner.blocks, check: Some(done) });
802 self
803 }
804}
805
806pub struct ImageBuilder {
808 width: Option<Length>,
809 align: Align,
810 caption: Option<Vec<Inline>>,
811 decor: ImageDecor,
812}
813
814impl ImageBuilder {
815 pub fn width_px(&mut self, px: f32) -> &mut Self {
817 self.width = Some(Length::Px(px));
818 self
819 }
820 pub fn width_percent(&mut self, pct: f32) -> &mut Self {
822 self.width = Some(Length::Percent(pct));
823 self
824 }
825 pub fn align(&mut self, a: Align) -> &mut Self {
827 self.align = a;
828 self
829 }
830 pub fn caption(&mut self, s: impl Into<String>) -> &mut Self {
832 self.caption = Some(vec![Inline::Text { text: s.into(), style: TextStyle::default() }]);
833 self
834 }
835 pub fn caption_with<R>(&mut self, f: impl FnOnce(&mut ParaBuilder) -> R) -> &mut Self {
837 let mut pb = ParaBuilder::new();
838 let _ = f(&mut pb);
839 self.caption = Some(pb.inlines);
840 self
841 }
842
843 pub fn badge<R>(
847 &mut self,
848 text: impl Into<String>,
849 f: impl FnOnce(&mut BadgeBuilder) -> R,
850 ) -> &mut Self {
851 let mut bb = BadgeBuilder { badge: Badge::new(text) };
852 let _ = f(&mut bb);
853 self.decor.badge = Some(bb.badge);
854 self
855 }
856 pub fn border(&mut self, width: f32, hex: &str) -> &mut Self {
858 if width > 0.0 && width.is_finite() {
859 if let Some(color) = Color::hex(hex) {
860 self.decor.border = Some(ImageBorder { width, color });
861 }
862 }
863 self
864 }
865 pub fn watermark<R>(
867 &mut self,
868 text: impl Into<String>,
869 f: impl FnOnce(&mut WatermarkBuilder) -> R,
870 ) -> &mut Self {
871 let mut wb = WatermarkBuilder { wm: Watermark::new(text) };
872 let _ = f(&mut wb);
873 self.decor.watermark = Some(wb.wm);
874 self
875 }
876 pub fn rounded(&mut self, radius: f32) -> &mut Self {
878 if radius.is_finite() && radius > 0.0 {
879 self.decor.radius = radius;
880 }
881 self
882 }
883 pub fn shadow(&mut self) -> &mut Self {
885 self.decor.shadow = Some(Shadow::default());
886 self
887 }
888 pub fn shadow_with(&mut self, dx: f32, dy: f32, blur: f32, hex: &str) -> &mut Self {
890 if let Some(color) = Color::hex(hex) {
891 self.decor.shadow = Some(Shadow { dx, dy, blur: blur.max(0.0), color });
892 }
893 self
894 }
895}
896
897pub struct BadgeBuilder {
899 badge: Badge,
900}
901
902impl BadgeBuilder {
903 pub fn anchor(&mut self, a: Anchor) -> &mut Self {
905 self.badge.anchor = a;
906 self
907 }
908 pub fn bg(&mut self, hex: &str) -> &mut Self {
910 if let Some(c) = Color::hex(hex) {
911 self.badge.bg = c;
912 }
913 self
914 }
915 pub fn fg(&mut self, hex: &str) -> &mut Self {
917 if let Some(c) = Color::hex(hex) {
918 self.badge.fg = c;
919 }
920 self
921 }
922 pub fn size(&mut self, mult: f32) -> &mut Self {
924 if mult.is_finite() && mult > 0.0 {
925 self.badge.size = mult;
926 }
927 self
928 }
929}
930
931pub struct WatermarkBuilder {
933 wm: Watermark,
934}
935
936impl WatermarkBuilder {
937 pub fn anchor(&mut self, a: Anchor) -> &mut Self {
939 self.wm.anchor = a;
940 self
941 }
942 pub fn color(&mut self, hex: &str) -> &mut Self {
944 if let Some(c) = Color::hex(hex) {
945 self.wm.color = c;
946 }
947 self
948 }
949 pub fn size(&mut self, mult: f32) -> &mut Self {
951 if mult.is_finite() && mult > 0.0 {
952 self.wm.size = mult;
953 }
954 self
955 }
956}
957