citum_engine/render/
latex.rs1use super::format::OutputFormat;
9use citum_schema::template::WrapPunctuation;
10
11#[derive(Debug, Clone, Default)]
13pub struct Latex;
14
15impl OutputFormat for Latex {
16 type Output = String;
17
18 fn text(&self, s: &str) -> Self::Output {
19 let mut res = String::with_capacity(s.len() + 10);
20 for c in s.chars() {
21 match c {
22 '\\' => res.push_str(r"\textbackslash{}"),
23 '{' => res.push_str(r"\{"),
24 '}' => res.push_str(r"\}"),
25 '$' => res.push_str(r"\$"),
26 '&' => res.push_str(r"\&"),
27 '#' => res.push_str(r"\#"),
28 '_' => res.push_str(r"\_"),
29 '%' => res.push_str(r"\%"),
30 '~' => res.push_str(r"\textasciitilde{}"),
31 '^' => res.push_str(r"\textasciicircum{}"),
32 _ => res.push(c),
33 }
34 }
35 res
36 }
37
38 fn join(&self, items: Vec<Self::Output>, delimiter: &str) -> Self::Output {
39 items.join(&self.text(delimiter))
40 }
41
42 fn finish(&self, output: Self::Output) -> String {
43 let mut result = String::with_capacity(output.len() + 4);
47 let mut prev = '\0';
48 for c in output.chars() {
49 if c == '&' && prev != '\\' {
50 result.push_str(r"\&");
51 } else {
52 result.push(c);
53 }
54 prev = c;
55 }
56 result
57 }
58
59 fn emph(&self, content: Self::Output) -> Self::Output {
60 format!(r"\textit{{{content}}}")
61 }
62
63 fn strong(&self, content: Self::Output) -> Self::Output {
64 format!(r"\textbf{{{content}}}")
65 }
66
67 fn small_caps(&self, content: Self::Output) -> Self::Output {
68 format!(r"\textsc{{{content}}}")
69 }
70
71 fn superscript(&self, content: Self::Output) -> Self::Output {
72 format!(r"\textsuperscript{{{content}}}")
73 }
74
75 fn quote(&self, content: Self::Output) -> Self::Output {
76 format!("``{content}''")
77 }
78
79 fn affix(&self, prefix: &str, content: Self::Output, suffix: &str) -> Self::Output {
80 format!("{}{}{}", self.text(prefix), content, self.text(suffix))
81 }
82
83 fn inner_affix(&self, prefix: &str, content: Self::Output, suffix: &str) -> Self::Output {
84 format!("{}{}{}", self.text(prefix), content, self.text(suffix))
85 }
86
87 fn wrap_punctuation(&self, wrap: &WrapPunctuation, content: Self::Output) -> Self::Output {
88 match wrap {
89 WrapPunctuation::Parentheses => format!("({content})"),
90 WrapPunctuation::Brackets => format!("[{content}]"),
91 WrapPunctuation::Quotes => self.quote(content),
92 }
93 }
94
95 fn semantic(&self, _class: &str, content: Self::Output) -> Self::Output {
96 content
99 }
100
101 fn annotation(&self, content: Self::Output) -> Self::Output {
102 if content.is_empty() {
103 return content;
104 }
105 format!(
106 "\n\\begin{{citumannotation}}\n{}\n\\end{{citumannotation}}",
107 content
108 )
109 }
110
111 fn link(&self, url: &str, content: Self::Output) -> Self::Output {
112 format!(r"\href{{{url}}}{{{content}}}")
113 }
114
115 fn bibliography(&self, entries: Vec<Self::Output>) -> Self::Output {
116 entries.join("\\par\\vspace{0.5em}")
117 }
118
119 fn entry(
120 &self,
121 _id: &str,
122 content: Self::Output,
123 _url: Option<&str>,
124 _metadata: &super::format::ProcEntryMetadata,
125 ) -> Self::Output {
126 format!("\\noindent\\hangindent=2em\\hangafter=1 {content}")
127 }
128}