livre/content/operators/text/
showing.rs1use std::fmt::Display;
4
5use enum_dispatch::enum_dispatch;
6use winnow::{combinator::peek, dispatch, token::any, BStr, PResult, Parser};
7
8use crate::{
9 content::state::TextObject,
10 extraction::{extract, Extract, HexadecimalString, LiteralString, PDFString},
11};
12
13use super::TextOperation;
14
15#[derive(Debug, Clone, PartialEq)]
18#[enum_dispatch(TextOperation)]
19pub enum TextShowingOperator {
20 ShowText(ShowText),
22 MoveToNextLineAndShowText(MoveToNextLineAndShowText),
24 MoveToNextLineAndShowTextWithSpacing(MoveToNextLineAndShowTextWithSpacing),
26 ShowTextArray(ShowTextArray),
28}
29
30impl Display for TextShowingOperator {
31 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
32 match self {
33 TextShowingOperator::ShowText(v) => write!(f, "{}", v),
34 TextShowingOperator::MoveToNextLineAndShowText(v) => writeln!(f, "{}", v),
35 TextShowingOperator::MoveToNextLineAndShowTextWithSpacing(v) => writeln!(f, "{}", v),
36 TextShowingOperator::ShowTextArray(v) => write!(f, "{}", v),
37 }
38 }
39}
40
41#[derive(Debug, Clone, PartialEq, Extract)]
47pub struct ShowText(PDFString);
48
49impl Display for ShowText {
50 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
51 write!(f, "{}", self.0)
52 }
53}
54
55impl TextOperation for ShowText {
56 fn apply(self, text_object: &mut TextObject) {
57 text_object.add_text(self.0);
58 }
59}
60
61#[derive(Debug, Clone, PartialEq, Extract)]
70pub struct MoveToNextLineAndShowText(PDFString);
71
72impl TextOperation for MoveToNextLineAndShowText {
73 fn apply(self, text_object: &mut TextObject) {
74 text_object.move_to_next_line();
75 text_object.add_text(self.0);
76 }
77}
78
79impl Display for MoveToNextLineAndShowText {
80 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
81 write!(f, "{}", self.0)
82 }
83}
84
85#[derive(Debug, Clone, PartialEq, Extract)]
91pub struct MoveToNextLineAndShowTextWithSpacing(f32, f32, PDFString);
92
93impl TextOperation for MoveToNextLineAndShowTextWithSpacing {
94 fn apply(self, text_object: &mut TextObject) {
95 let MoveToNextLineAndShowTextWithSpacing(aw, ac, text) = self;
96
97 text_object.move_to_next_line();
98 text_object.set_word_spacing(aw);
99 text_object.set_character_spacing(ac);
100 text_object.add_text(text);
101 }
102}
103
104impl Display for MoveToNextLineAndShowTextWithSpacing {
105 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
106 write!(f, "{}", self.0)
107 }
108}
109
110#[derive(Debug, Clone, PartialEq, Extract)]
125pub struct ShowTextArray(Vec<TextArrayElement>);
126
127impl TextOperation for ShowTextArray {
128 fn apply(self, text_object: &mut TextObject) {
129 text_object.add_text_array(self.0);
130 }
131}
132
133impl Display for ShowTextArray {
134 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
135 for element in &self.0 {
136 if let TextArrayElement::Text(v) = element {
137 write!(f, "{}", v)?;
138 }
139 }
140 Ok(())
141 }
142}
143
144#[derive(Debug, Clone, PartialEq)]
146pub enum TextArrayElement {
147 Text(PDFString),
148 Offset(f32),
149}
150
151impl From<PDFString> for TextArrayElement {
152 fn from(value: PDFString) -> Self {
153 Self::Text(value)
154 }
155}
156
157impl From<&str> for TextArrayElement {
158 fn from(value: &str) -> Self {
159 Self::Text(value.into())
160 }
161}
162
163impl From<HexadecimalString> for TextArrayElement {
164 fn from(value: HexadecimalString) -> Self {
165 Self::Text(value.into())
166 }
167}
168
169impl From<LiteralString> for TextArrayElement {
170 fn from(value: LiteralString) -> Self {
171 Self::Text(value.into())
172 }
173}
174
175impl From<f32> for TextArrayElement {
176 fn from(value: f32) -> Self {
177 Self::Offset(value)
178 }
179}
180
181impl Extract<'_> for TextArrayElement {
182 fn extract(input: &mut &BStr) -> PResult<Self> {
183 dispatch! {peek(any);
184 b'(' => LiteralString::extract.map(TextArrayElement::from),
185 b'<' => HexadecimalString::extract.map(TextArrayElement::from),
186 _ => extract.map(TextArrayElement::Offset),
187 }
188 .parse_next(input)
189 }
190}
191
192#[cfg(test)]
193mod tests {
194 use std::fmt::Debug;
195
196 use super::TextArrayElement::*;
197 use super::*;
198
199 use indoc::indoc;
200 use rstest::rstest;
201
202 #[rstest]
203 #[case(
204 indoc!{br#"
205 [ (&''!\(\)) 7 (*+) -4 (,) -8 (-) 6 (!\(.) 3 (-) -7 (.\(/) 3 ] TJ
206 "#},
207 ShowTextArray(vec![
208 "&''!()".into(),
209 Offset(7.0),
210 "*+".into(),
211 Offset(-4.0),
212 ",".into(),
213 Offset(-8.0),
214 "-".into(),
215 Offset(6.0),
216 "!(.".into(),
217 Offset(3.0),
218 "-".into(),
219 Offset(-7.0),
220 ".(/".into(),
221 Offset(3.0),
222 ])
223 )]
224 fn extraction<'de, T>(#[case] input: &'de [u8], #[case] expected: T)
225 where
226 T: Extract<'de> + Debug + PartialEq,
227 {
228 let result = extract(&mut input.as_ref()).unwrap();
229 assert_eq!(expected, result);
230 }
231}