#![warn(
missing_docs,
clippy::all,
clippy::correctness,
clippy::suspicious,
clippy::style,
clippy::complexity,
clippy::perf,
clippy::pedantic,
clippy::cargo,
clippy::nursery
)]
#![allow(
missing_docs, )]
mod imp;
#[cfg(feature = "pdf")]
mod pdf;
#[cfg(feature = "png")]
mod png;
#[cfg(feature = "svg")]
mod svg;
use font::Font;
use geom::{Point, Rect, Size};
use key::Key;
use profile::Profile;
#[allow(unused_imports)] pub(crate) use imp::{KeyDrawing, Path};
#[derive(Debug, Clone)]
#[allow(dead_code)] pub struct Drawing {
bounds: Rect,
keys: Vec<KeyDrawing>,
scale: f64,
}
impl Drawing {
#[must_use]
pub fn new(keys: &[Key], options: &Options) -> Self {
let bounds = keys
.iter()
.map(|k| k.shape.outer_rect().with_origin(k.position))
.fold(
Rect::from_origin_size(Point::ORIGIN, Size::new(1., 1.)),
|rect, key| rect.union(key),
);
let keys = keys
.iter()
.map(|key| KeyDrawing::new(key, options))
.collect();
Self {
bounds,
keys,
scale: options.scale,
}
}
#[cfg(feature = "pdf")]
#[must_use]
pub fn to_svg(&self) -> String {
svg::draw(self)
}
#[cfg(feature = "pdf")]
#[must_use]
pub fn to_png(&self, dpi: f64) -> Vec<u8> {
png::draw(self, dpi)
}
#[cfg(feature = "pdf")]
#[must_use]
pub fn to_pdf(&self) -> Vec<u8> {
pdf::draw(self)
}
#[cfg(feature = "pdf")]
#[must_use]
pub fn to_ai(&self) -> Vec<u8> {
pdf::draw(self)
}
}
#[derive(Debug, Clone)]
pub struct Options<'a> {
profile: &'a Profile,
font: &'a Font,
scale: f64,
outline_width: f64,
show_keys: bool,
show_margin: bool,
}
impl<'a> Default for Options<'a> {
fn default() -> Self {
Self {
profile: &Profile::DEFAULT,
font: Font::default_ref(),
scale: 1.0,
outline_width: 10.0,
show_keys: true,
show_margin: false,
}
}
}
impl<'a> Options<'a> {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub const fn profile(self, profile: &'a Profile) -> Self {
Self { profile, ..self }
}
#[must_use]
pub const fn font(self, font: &'a Font) -> Self {
Self { font, ..self }
}
#[must_use]
pub const fn scale(self, scale: f64) -> Self {
Self { scale, ..self }
}
#[must_use]
pub const fn outline_width(self, outline_width: f64) -> Self {
Self {
outline_width,
..self
}
}
#[must_use]
pub const fn show_keys(self, show_keys: bool) -> Self {
Self { show_keys, ..self }
}
#[must_use]
pub const fn show_margin(self, show_margin: bool) -> Self {
Self {
show_margin,
..self
}
}
#[must_use]
pub fn draw(&self, keys: &[Key]) -> Drawing {
Drawing::new(keys, self)
}
}
#[cfg(test)]
mod tests {
use assert_approx_eq::assert_approx_eq;
use profile::Profile;
use super::*;
#[test]
fn test_drawing_options() {
let options = Options::default();
assert_approx_eq!(options.scale, 1.);
assert_eq!(options.font.num_glyphs(), 1); let profile = Profile::default();
let font = Font::from_ttf(std::fs::read(env!("DEMO_TTF")).unwrap()).unwrap();
let options = Options::new()
.profile(&profile)
.font(&font)
.scale(2.)
.outline_width(20.)
.show_keys(false)
.show_margin(true);
assert_eq!(options.profile.typ.depth(), 1.0);
assert_eq!(options.font.num_glyphs(), 3); assert_eq!(options.scale, 2.0);
}
#[test]
fn test_drawing_options_draw() {
let options = Options::new();
let keys = [Key::example()];
let drawing = options.draw(&keys);
assert_eq!(drawing.bounds.width(), 1.);
assert_eq!(drawing.bounds.height(), 1.);
assert_eq!(drawing.keys.len(), 1);
assert_eq!(drawing.scale, options.scale);
}
}