// font-kit/examples/render-glyph.rs // // Copyright © 2018 The Pathfinder Project Developers. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. extern crate clap; extern crate colored; extern crate font_kit; extern crate pathfinder_geometry; use clap::{Arg, ArgAction, ArgGroup, ArgMatches, Command}; use colored::Colorize; use font_kit::canvas::{Canvas, Format, RasterizationOptions}; use font_kit::hinting::HintingOptions; use font_kit::source::SystemSource; use pathfinder_geometry::transform2d::Transform2F; use std::fmt::Write; #[cfg(any(target_family = "windows", target_os = "macos"))] static SANS_SERIF_FONT_REGULAR_POSTSCRIPT_NAME: &'static str = "ArialMT"; #[cfg(not(any(target_family = "windows", target_os = "macos")))] static SANS_SERIF_FONT_REGULAR_POSTSCRIPT_NAME: &str = "DejaVuSans"; fn get_args() -> ArgMatches { let postscript_name_arg = Arg::new("POSTSCRIPT-NAME") .help("PostScript name of the font") .default_value(SANS_SERIF_FONT_REGULAR_POSTSCRIPT_NAME) .index(1); let glyph_arg = Arg::new("GLYPH") .help("Character to render") .default_value("A") .index(2); let size_arg = Arg::new("SIZE") .help("Font size in blocks") .default_value("32") .index(3); let grayscale_arg = Arg::new("grayscale") .long("grayscale") .help("Use grayscale antialiasing (default)"); let bilevel_arg = Arg::new("bilevel") .help("Use bilevel (black & white) rasterization") .short('b') .long("bilevel") .action(ArgAction::SetTrue); let subpixel_arg = Arg::new("subpixel") .help("Use subpixel (LCD) rasterization") .short('s') .long("subpixel") .action(ArgAction::SetTrue); let hinting_value_parser = clap::builder::PossibleValuesParser::new(["none", "vertical", "full"]); let hinting_arg = Arg::new("hinting") .help("Select hinting type") .short('H') .long("hinting") .value_parser(hinting_value_parser) .value_names(&["TYPE"]); let transform_arg = Arg::new("transform") .help("Transform to apply to glyph when rendering") .long("transform") .num_args(4); let rasterization_mode_group = ArgGroup::new("rasterization-mode").args(&["grayscale", "bilevel", "subpixel"]); Command::new("render-glyph") .version("0.1") .author("The Pathfinder Project Developers") .about("Simple example tool to render glyphs with `font-kit`") .arg(postscript_name_arg) .arg(glyph_arg) .arg(size_arg) .arg(grayscale_arg) .arg(bilevel_arg) .arg(subpixel_arg) .group(rasterization_mode_group) .arg(hinting_arg) .arg(transform_arg) .get_matches() } fn main() { let matches = get_args(); let postscript_name = matches .get_one::("POSTSCRIPT-NAME") .map(|s| s.as_str()) .unwrap(); let character = matches .get_one::("GLYPH") .map(|s| s.as_str()) .unwrap() .chars() .next() .unwrap(); let size: f32 = matches .get_one::("SIZE") .map(|s| s.as_str()) .unwrap() .parse() .unwrap(); let (canvas_format, rasterization_options) = if matches.get_flag("bilevel") { (Format::A8, RasterizationOptions::Bilevel) } else if matches.get_flag("subpixel") { (Format::Rgb24, RasterizationOptions::SubpixelAa) } else { (Format::A8, RasterizationOptions::GrayscaleAa) }; let mut transform = Transform2F::default(); if let Some(values) = matches.get_many::("transform") { if let [Ok(a), Ok(b), Ok(c), Ok(d)] = values.map(|x| x.parse()).collect::>()[..] { transform = Transform2F::row_major(a, b, c, d, 0.0, 0.0) } } let hinting_options = match matches.get_one::("hinting").map(|s| s.as_str()) { Some("vertical") => HintingOptions::Vertical(size), Some("full") => HintingOptions::Full(size), _ => HintingOptions::None, }; let font = SystemSource::new() .select_by_postscript_name(postscript_name) .unwrap() .load() .unwrap(); let glyph_id = font.glyph_for_char(character).unwrap(); let raster_rect = font .raster_bounds( glyph_id, size, transform, hinting_options, rasterization_options, ) .unwrap(); let mut canvas = Canvas::new(raster_rect.size(), canvas_format); font.rasterize_glyph( &mut canvas, glyph_id, size, Transform2F::from_translation(-raster_rect.origin().to_f32()) * transform, hinting_options, rasterization_options, ) .unwrap(); println!("glyph {}:", glyph_id); for y in 0..raster_rect.height() { let mut line = String::new(); let (row_start, row_end) = (y as usize * canvas.stride, (y + 1) as usize * canvas.stride); let row = &canvas.pixels[row_start..row_end]; for x in 0..raster_rect.width() { match canvas.format { Format::Rgba32 => unimplemented!(), Format::Rgb24 => { write!( &mut line, "{}{}{}", shade(row[x as usize * 3]).to_string().red(), shade(row[x as usize * 3 + 1]).to_string().green(), shade(row[x as usize * 3 + 2]).to_string().blue() ) .unwrap(); } Format::A8 => { let shade = shade(row[x as usize]); line.push(shade); line.push(shade); } } } println!("{}", line); } } fn shade(value: u8) -> char { match value { 0 => ' ', 1..=84 => '░', 85..=169 => '▒', 170..=254 => '▓', _ => '█', } }