pdf_canvas/
textobject.rs

1use crate::encoding::{Encoding, WIN_ANSI_ENCODING};
2use crate::fontref::FontRef;
3use crate::graphicsstate::Color;
4use std::io::{self, Write};
5
6/// A text object is where text is put on the canvas.
7///
8/// A TextObject should never be created directly by the user.
9/// Instead, the [Canvas.text](struct.Canvas.html#method.text) method
10/// should be called.
11/// It will create a TextObject and call a callback, before terminating
12/// the text object properly.
13///
14/// # Example
15///
16/// ```
17/// # use pdf_canvas::{Pdf, BuiltinFont, FontSource};
18/// # use pdf_canvas::graphicsstate::Matrix;
19/// # let mut document = Pdf::create("foo.pdf").unwrap();
20/// # document.render_page(180.0, 240.0, |canvas| {
21/// let serif = canvas.get_font(BuiltinFont::Times_Roman);
22/// // t will be a TextObject
23/// canvas.text(|t| {
24///     t.set_font(&serif, 14.0)?;
25///     t.set_leading(18.0)?;
26///     t.pos(10.0, 300.0)?;
27///     t.show("Some lines of text in what might look like a")?;
28///     t.show_line("paragraph of three lines. Lorem ipsum dolor")?;
29///     t.show_line("sit amet. Blahonga.")?;
30///     Ok(())
31/// })?;
32/// # Ok(())
33/// # }).unwrap();
34/// # document.finish().unwrap();
35/// ```
36pub struct TextObject<'a> {
37    output: &'a mut dyn Write,
38    encoding: Encoding,
39}
40
41impl<'a> TextObject<'a> {
42    // Should not be called by user code.
43    pub(crate) fn new(output: &'a mut dyn Write) -> Self {
44        TextObject {
45            output,
46            encoding: WIN_ANSI_ENCODING.clone(),
47        }
48    }
49
50    /// Set the font and font-size to be used by the following text
51    /// operations.
52    pub fn set_font(&mut self, font: &FontRef, size: f32) -> io::Result<()> {
53        self.encoding = font.get_encoding().clone();
54        writeln!(self.output, "{} {} Tf", font, size)
55    }
56    /// Set leading, the vertical distance from a line of text to the next.
57    /// This is important for the [show_line](#method.show_line) method.
58    pub fn set_leading(&mut self, leading: f32) -> io::Result<()> {
59        writeln!(self.output, "{} TL", leading)
60    }
61    /// Set the rise above the baseline for coming text.  Calling
62    /// set_rise again with a zero argument will get back to the old
63    /// baseline.
64    pub fn set_rise(&mut self, rise: f32) -> io::Result<()> {
65        writeln!(self.output, "{} Ts", rise)
66    }
67    /// Set the amount of extra space between characters, in 1/1000
68    /// text unit.
69    pub fn set_char_spacing(&mut self, a_c: f32) -> io::Result<()> {
70        writeln!(self.output, "{} Tc", a_c)
71    }
72    /// Set the amount of extra space between words, in 1/1000
73    /// text unit.
74    pub fn set_word_spacing(&mut self, a_w: f32) -> io::Result<()> {
75        writeln!(self.output, "{} Tw", a_w)
76    }
77
78    /// Set color for stroking operations.
79    pub fn set_stroke_color(&mut self, color: Color) -> io::Result<()> {
80        let norm = |c| f32::from(c) / 255.0;
81        match color {
82            Color::RGB { red, green, blue } => writeln!(
83                self.output,
84                "{} {} {} SC",
85                norm(red),
86                norm(green),
87                norm(blue),
88            ),
89            Color::Gray { gray } => writeln!(self.output, "{} G", norm(gray)),
90        }
91    }
92    /// Set color for non-stroking operations.
93    pub fn set_fill_color(&mut self, color: Color) -> io::Result<()> {
94        let norm = |c| f32::from(c) / 255.0;
95        match color {
96            Color::RGB { red, green, blue } => writeln!(
97                self.output,
98                "{} {} {} sc",
99                norm(red),
100                norm(green),
101                norm(blue),
102            ),
103            Color::Gray { gray } => writeln!(self.output, "{} g", norm(gray)),
104        }
105    }
106
107    /// Move text position.
108    ///
109    /// The first time `pos` is called in a
110    /// TextObject, (x, y) refers to the same point as for
111    /// [Canvas::move_to](struct.Canvas.html#method.move_to), after that,
112    /// the point is relative to the earlier pos.
113    pub fn pos(&mut self, x: f32, y: f32) -> io::Result<()> {
114        writeln!(self.output, "{} {} Td", x, y)
115    }
116    /// Show a text.
117    pub fn show(&mut self, text: &str) -> io::Result<()> {
118        write!(self.output, "(")?;
119        self.output.write_all(&self.encoding.encode_string(text))?;
120        writeln!(self.output, ") Tj")
121    }
122
123    /// Show one or more text strings, allowing individual glyph positioning.
124    ///
125    /// Each item in param should contain a string to show and a number
126    /// to adjust the position.
127    /// The adjustment is measured in thousands of unit of text space.
128    /// Positive adjustment brings letters closer, negative widens the gap.
129    ///
130    /// # Example
131    ///
132    /// ```
133    /// # use pdf_canvas::{Pdf, BuiltinFont, FontSource};
134    /// # use pdf_canvas::graphicsstate::Matrix;
135    /// # let mut document = Pdf::create("foo.pdf").unwrap();
136    /// # document.render_page(180.0, 240.0, |canvas| {
137    /// # let serif = canvas.get_font(BuiltinFont::Times_Roman);
138    /// # canvas.text(|t| {
139    /// #    t.set_font(&serif, 14.0)?;
140    /// t.show_adjusted(&[("W", 130), ("AN", -40), ("D", 0)])
141    /// # })
142    /// # }).unwrap();
143    /// # document.finish().unwrap();
144    /// ```
145    pub fn show_adjusted(&mut self, param: &[(&str, i32)]) -> io::Result<()> {
146        write!(self.output, "[")?;
147        for &(text, offset) in param {
148            write!(self.output, "(")?;
149            self.output.write_all(&self.encoding.encode_string(text))?;
150            write!(self.output, ") {} ", offset)?
151        }
152        writeln!(self.output, "] TJ")
153    }
154    /// Show a text as a line.  See also [set_leading](#method.set_leading).
155    pub fn show_line(&mut self, text: &str) -> io::Result<()> {
156        write!(self.output, "(")?;
157        self.output.write_all(&self.encoding.encode_string(text))?;
158        writeln!(self.output, ") '")
159    }
160    /// Push the graphics state on a stack.
161    pub fn gsave(&mut self) -> io::Result<()> {
162        // TODO Push current encoding in self?
163        writeln!(self.output, "q")
164    }
165    /// Pop a graphics state from the [gsave](#method.gsave) stack and
166    /// restore it.
167    pub fn grestore(&mut self) -> io::Result<()> {
168        // TODO Pop current encoding in self?
169        writeln!(self.output, "Q")
170    }
171}