1use crate::error::{Error, Result};
2use crate::instruction::{EscposImage, ImageOptions};
3use crate::printer::{Printer, PrinterDevice};
4use crate::pulldown_cmark_ext::{EventExt, TagExt};
5use crate::style::{StyleSheet, StyleTag};
6use pulldown_cmark::{Event, Tag};
7
8#[derive(Debug, Clone, Default)]
9pub struct MarkdownRenderOptions {
10 pub styles: StyleSheet,
11 pub image: ImageOptions,
12}
13
14#[derive(Debug, Clone, Copy)]
15pub enum TagState {
16 Stateless,
17 Item(u64),
18}
19
20impl TagState {
21 pub fn num(&self) -> Option<u64> {
22 match self {
23 Self::Stateless => None,
24 Self::Item(num) => Some(*num),
25 }
26 }
27}
28
29#[derive(Default)]
30struct RendererState<'a> {
31 tree: Vec<(Tag<'a>, TagState)>,
32}
33
34impl<'a> RendererState<'a> {
35 fn push_tag(&mut self, tag: Tag<'a>) -> Result<()> {
36 let state = match tag {
37 Tag::List(Some(num)) => TagState::Item(num),
38 Tag::Item => {
39 match self
40 .tag_state_mut()
41 .ok_or_else(|| Error::UnexpectedTag(tag.clone().to_static()))?
42 {
43 TagState::Stateless => TagState::Stateless,
44 TagState::Item(num) => {
45 let state = TagState::Item(*num);
46 *num += 1;
47 state
48 }
49 }
50 }
51 _ => TagState::Stateless,
52 };
53 self.tree.push((tag, state));
54 Ok(())
55 }
56
57 fn pop_tag(&mut self, tag: &Tag<'a>) -> Result<()> {
58 if self.tag() != Some(tag) {
59 Err(Error::UnexpectedTag(tag.clone().to_static()))
60 } else {
61 self.tree.pop();
62 Ok(())
63 }
64 }
65
66 fn tag(&self) -> Option<&Tag<'a>> {
67 self.tree.last().map(|item| &item.0)
68 }
69
70 fn tag_state(&self) -> Option<&TagState> {
71 self.tree.last().map(|item| &item.1)
72 }
73
74 fn tag_state_mut(&mut self) -> Option<&mut TagState> {
75 self.tree.last_mut().map(|item| &mut item.1)
76 }
77
78 fn style_tags(&self) -> Result<Vec<StyleTag>> {
79 self.tree.iter().map(|(tag, _)| tag.style_tag()).collect()
80 }
81}
82
83impl<D> Printer<D>
84where
85 D: PrinterDevice,
86{
87 pub fn markdown<'a, I>(&mut self, iter: I, opts: &MarkdownRenderOptions) -> Result<&mut Self>
88 where
89 I: Iterator<Item = Event<'a>>,
90 {
91 let mut state = RendererState::default();
92 for event in iter {
93 match event {
94 Event::Start(tag) => {
95 state.push_tag(tag.clone())?;
96 let style_tags = state.style_tags()?;
97 let style = opts.styles.get(&style_tags);
98 self.font_style(&style)?;
99 self.begin_block_style(&style, state.tag_state())?;
100
101 match tag {
102 Tag::Image(_, filename, _) => {
103 let img = image::open(filename.as_ref())?;
104 let escpos_img = EscposImage::new(&img, &opts.image);
105 self.image(&escpos_img)?;
106
107 let mut img_caption_tags = style_tags;
108 img_caption_tags.push(StyleTag::ImgCaption);
109 let img_caption_style = opts.styles.get(&img_caption_tags);
110 self.font_style(&img_caption_style)?;
111 self.begin_block_style(&img_caption_style, None)?;
112 }
113 _ => {}
114 }
115 }
116 Event::End(tag) => {
117 let style_tags = state.style_tags()?;
118 match tag {
119 Tag::Image(..) => {
120 let mut img_caption_tags = style_tags.clone();
121 img_caption_tags.push(StyleTag::ImgCaption);
122 let img_caption_style = opts.styles.get(&img_caption_tags);
123 self.end_block_style(&img_caption_style)?;
124 }
125 _ => {}
126 }
127 let style = opts.styles.get(&style_tags);
128 self.end_block_style(&style)?;
129 state.pop_tag(&tag)?;
130 let style = opts.styles.get(&state.style_tags()?);
131 self.font_style(&style)?;
132 }
133 Event::Text(text) => {
134 self.print(text)?;
135 }
136 Event::Code(text) => {
137 let mut style_tags = state.style_tags()?;
138 style_tags.push(StyleTag::Code);
139 let style = opts.styles.get(&style_tags);
140 self.font_style(&style)?;
141 self.begin_block_style(&style, None)?;
142
143 self.print(text)?;
144
145 self.end_block_style(&style)?;
146 let style = opts.styles.get(&state.style_tags()?);
147 self.font_style(&style)?;
148 }
149 Event::SoftBreak => {
150 self.print(" ")?;
151 }
152 Event::HardBreak => {
153 self.println("")?;
154 }
155 Event::Rule => {
156 let mut style_tags = state.style_tags()?;
157 style_tags.push(StyleTag::Hr);
158 let style = opts.styles.get(&style_tags);
159 self.font_style(&style)?;
160 self.begin_block_style(&style, None)?;
161
162 let num_bars = self.printable_width() / self.calc_char_size();
163 self.println(vec!["─"; num_bars].join(""))?;
164
165 self.end_block_style(&style)?;
166 let style = opts.styles.get(&state.style_tags()?);
167 self.font_style(&style)?;
168 }
169 event => return Err(Error::MarkdownEventUnimplemented(event.to_static())),
170 }
171 }
172 Ok(self)
173 }
174}