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