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"\emph{{{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_marks(&self, depth: usize) -> (&'static str, &'static str) {
76 if depth.is_multiple_of(2) {
77 ("``", "''")
78 } else {
79 ("`", "'")
80 }
81 }
82
83 fn quote(&self, content: Self::Output) -> Self::Output {
84 format!("``{content}''")
85 }
86
87 fn affix(&self, prefix: &str, content: Self::Output, suffix: &str) -> Self::Output {
88 format!("{}{}{}", self.text(prefix), content, self.text(suffix))
89 }
90
91 fn inner_affix(&self, prefix: &str, content: Self::Output, suffix: &str) -> Self::Output {
92 format!("{}{}{}", self.text(prefix), content, self.text(suffix))
93 }
94
95 fn wrap_punctuation(&self, wrap: &WrapPunctuation, content: Self::Output) -> Self::Output {
96 match wrap {
97 WrapPunctuation::Parentheses => format!("({content})"),
98 WrapPunctuation::Brackets => format!("[{content}]"),
99 WrapPunctuation::Quotes => self.quote(content),
100 }
101 }
102
103 fn semantic(&self, _class: &str, content: Self::Output) -> Self::Output {
104 content
107 }
108
109 fn annotation(&self, content: Self::Output) -> Self::Output {
110 if content.is_empty() {
111 return content;
112 }
113 format!(
114 "\n\\begin{{citumannotation}}\n{}\n\\end{{citumannotation}}",
115 content
116 )
117 }
118
119 fn link(&self, url: &str, content: Self::Output) -> Self::Output {
120 format!(r"\href{{{url}}}{{{content}}}")
121 }
122
123 fn bibliography(&self, entries: Vec<Self::Output>) -> Self::Output {
124 entries.join("\\par\\vspace{0.5em}")
125 }
126
127 fn entry(
128 &self,
129 _id: &str,
130 content: Self::Output,
131 _url: Option<&str>,
132 _metadata: &super::format::ProcEntryMetadata,
133 ) -> Self::Output {
134 format!("\\noindent\\hangindent=2em\\hangafter=1 {content}")
135 }
136}