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}