use crate::fontref::FontRef;
use crate::fontsource::{BuiltinFont, FontSource};
use crate::graphicsstate::*;
use crate::outline::OutlineItem;
use crate::textobject::TextObject;
use std::collections::HashMap;
use std::io::{self, Write};
use std::sync::Arc;
pub struct Canvas<'a> {
output: &'a mut dyn Write,
fonts: &'a mut HashMap<BuiltinFont, FontRef>,
outline_items: &'a mut Vec<OutlineItem>,
}
impl<'a> Canvas<'a> {
pub(crate) fn new(
output: &'a mut dyn Write,
fonts: &'a mut HashMap<BuiltinFont, FontRef>,
outline_items: &'a mut Vec<OutlineItem>,
) -> Self {
Canvas {
output,
fonts,
outline_items,
}
}
pub fn rectangle(
&mut self,
x: f32,
y: f32,
width: f32,
height: f32,
) -> io::Result<()> {
writeln!(self.output, "{} {} {} {} re", x, y, width, height)
}
pub fn set_line_join_style(
&mut self,
style: JoinStyle,
) -> io::Result<()> {
writeln!(
self.output,
"{} j",
match style {
JoinStyle::Miter => 0,
JoinStyle::Round => 1,
JoinStyle::Bevel => 2,
}
)
}
pub fn set_line_cap_style(&mut self, style: CapStyle) -> io::Result<()> {
writeln!(
self.output,
"{} J",
match style {
CapStyle::Butt => 0,
CapStyle::Round => 1,
CapStyle::ProjectingSquare => 2,
}
)
}
pub fn set_line_width(&mut self, w: f32) -> io::Result<()> {
writeln!(self.output, "{} w", w)
}
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)),
}
}
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)),
}
}
pub fn concat(&mut self, m: Matrix) -> io::Result<()> {
writeln!(self.output, "{} cm", m)
}
pub fn line(
&mut self,
x1: f32,
y1: f32,
x2: f32,
y2: f32,
) -> io::Result<()> {
self.move_to(x1, y1)?;
self.line_to(x2, y2)
}
pub fn move_to(&mut self, x: f32, y: f32) -> io::Result<()> {
write!(self.output, "{} {} m ", x, y)
}
pub fn line_to(&mut self, x: f32, y: f32) -> io::Result<()> {
write!(self.output, "{} {} l ", x, y)
}
pub fn curve_to(
&mut self,
x1: f32,
y1: f32,
x2: f32,
y2: f32,
x3: f32,
y3: f32,
) -> io::Result<()> {
writeln!(self.output, "{} {} {} {} {} {} c", x1, y1, x2, y2, x3, y3)
}
pub fn circle(&mut self, x: f32, y: f32, r: f32) -> io::Result<()> {
let top = y - r;
let bottom = y + r;
let left = x - r;
let right = x + r;
#[allow(clippy::excessive_precision)]
let c = 0.551_915_024_494;
let dist = r * c;
let up = y - dist;
let down = y + dist;
let leftp = x - dist;
let rightp = x + dist;
self.move_to(x, top)?;
self.curve_to(leftp, top, left, up, left, y)?;
self.curve_to(left, down, leftp, bottom, x, bottom)?;
self.curve_to(rightp, bottom, right, down, right, y)?;
self.curve_to(right, up, rightp, top, x, top)
}
pub fn stroke(&mut self) -> io::Result<()> {
writeln!(self.output, "S")
}
pub fn close_and_stroke(&mut self) -> io::Result<()> {
writeln!(self.output, "s")
}
pub fn fill(&mut self) -> io::Result<()> {
writeln!(self.output, "f")
}
pub fn get_font(&mut self, font: BuiltinFont) -> FontRef {
let next_n = self.fonts.len();
self.fonts
.entry(font)
.or_insert_with(|| {
FontRef::new(
next_n,
font.get_encoding().clone(),
Arc::new(font.get_metrics()),
)
})
.clone()
}
pub fn text<F, T>(&mut self, render_text: F) -> io::Result<T>
where
F: FnOnce(&mut TextObject) -> io::Result<T>,
{
writeln!(self.output, "BT")?;
let result = render_text(&mut TextObject::new(self.output))?;
writeln!(self.output, "ET")?;
Ok(result)
}
pub fn left_text(
&mut self,
x: f32,
y: f32,
font: BuiltinFont,
size: f32,
text: &str,
) -> io::Result<()> {
let font = self.get_font(font);
self.text(|t| {
t.set_font(&font, size)?;
t.pos(x, y)?;
t.show(text)
})
}
pub fn right_text(
&mut self,
x: f32,
y: f32,
font: BuiltinFont,
size: f32,
text: &str,
) -> io::Result<()> {
let font = self.get_font(font);
self.text(|t| {
let text_width = font.get_width(size, text);
t.set_font(&font, size)?;
t.pos(x - text_width, y)?;
t.show(text)
})
}
pub fn center_text(
&mut self,
x: f32,
y: f32,
font: BuiltinFont,
size: f32,
text: &str,
) -> io::Result<()> {
let font = self.get_font(font);
self.text(|t| {
let text_width = font.get_width(size, text);
t.set_font(&font, size)?;
t.pos(x - text_width / 2.0, y)?;
t.show(text)
})
}
pub fn add_outline(&mut self, title: &str) {
self.outline_items.push(OutlineItem::new(title));
}
pub fn gsave(&mut self) -> io::Result<()> {
writeln!(self.output, "q")
}
pub fn grestore(&mut self) -> io::Result<()> {
writeln!(self.output, "Q")
}
}