diff --git a/src/bin/silicon/config.rs b/src/bin/silicon/config.rs index 516dde3..9c64cf1 100644 --- a/src/bin/silicon/config.rs +++ b/src/bin/silicon/config.rs @@ -2,6 +2,7 @@ use anyhow::{Context, Error}; use clipboard::{ClipboardContext, ClipboardProvider}; use image::Rgba; use silicon::directories::PROJECT_DIRS; +use silicon::font::FontStyle; use silicon::formatter::{ImageFormatter, ImageFormatterBuilder}; use silicon::utils::{Background, ShadowAdder, ToRgba}; use std::ffi::OsString; @@ -57,6 +58,17 @@ fn parse_font_str(s: &str) -> Vec<(String, f32)> { result } +fn parse_font_style_str(s: &str) -> FontStyle { + let s = s.to_lowercase(); + match s.as_str() { + "regular" => FontStyle::REGULAR, + "italic" => FontStyle::ITALIC, + "bold" => FontStyle::BOLD, + "bolditalic" => FontStyle::BOLDITALIC, + _ => panic!("parse font_style error"), + } +} + fn parse_line_range(s: &str) -> Result, ParseIntError> { let mut result = vec![]; for range in s.split(';') { @@ -113,6 +125,10 @@ pub struct Config { #[structopt(long, short, value_name = "FONT", parse(from_str = parse_font_str))] pub font: Option, + /// The font style. eg. 'regular' + #[structopt(long, value_name = "FONT_STYLE",default_value="regular", parse(from_str = parse_font_style_str))] + pub font_style: FontStyle, + /// Lines to high light. rg. '1-3; 4' #[structopt(long, value_name = "LINES", parse(try_from_str = parse_line_range))] pub highlight_lines: Option, @@ -276,6 +292,7 @@ impl Config { .window_title(self.window_title.clone()) .line_number(!self.no_line_number) .font(self.font.clone().unwrap_or_default()) + .font_style(self.font_style.clone()) .round_corner(!self.no_round_corner) .shadow_adder(self.get_shadow_adder()?) .tab_width(self.tab_width) diff --git a/src/bin/silicon/main.rs b/src/bin/silicon/main.rs index b03408b..8a82fc6 100644 --- a/src/bin/silicon/main.rs +++ b/src/bin/silicon/main.rs @@ -121,7 +121,7 @@ fn run() -> Result<(), Error> { let mut formatter = config.get_formatter()?; - let image = formatter.format(&highlight, &theme); + let image = formatter.format(&highlight, &theme, config.font_style); if config.to_clipboard { dump_image_to_clipboard(&image)?; diff --git a/src/font.rs b/src/font.rs index 7f8e43c..56dd1c9 100644 --- a/src/font.rs +++ b/src/font.rs @@ -7,7 +7,7 @@ //! use silicon::font::{FontCollection, FontStyle}; //! //! let mut image = RgbImage::new(250, 100); -//! let font = FontCollection::new(&[("Hack", 27.0), ("FiraCode", 27.0)]).unwrap(); +//! let font = FontCollection::new(&[("Hack", 27.0), ("FiraCode", 27.0)],FontStyle::REGULAR).unwrap(); //! //! font.draw_text_mut(&mut image, Rgb([255, 0, 0]), 0, 0, FontStyle::REGULAR, "Hello, world"); //! ``` @@ -30,8 +30,9 @@ use std::sync::Arc; use syntect::highlighting; /// Font style -#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Default)] pub enum FontStyle { + #[default] REGULAR, ITALIC, BOLD, @@ -63,6 +64,7 @@ use FontStyle::*; pub struct ImageFont { pub fonts: HashMap, pub size: f32, + pub style: FontStyle, } impl Default for ImageFont { @@ -92,16 +94,21 @@ impl Default for ImageFont { fonts.insert(style, font); } - Self { fonts, size: 26.0 } + Self { + fonts, + size: 26.0, + ..Default::default() + } } } impl ImageFont { - pub fn new(name: &str, size: f32) -> Result { + pub fn new(name: &str, size: f32, style: FontStyle) -> Result { // Silicon already contains Hack font if name == "Hack" { let font = ImageFont { size, + style, ..Default::default() }; return Ok(font); @@ -145,7 +152,7 @@ impl ImageFont { } } - Ok(Self { fonts, size }) + Ok(Self { fonts, size, style }) } /// Get a font by style. If there is no such a font, it will return the REGULAR font. @@ -162,7 +169,8 @@ impl ImageFont { /// Get the height of the font pub fn get_font_height(&self) -> u32 { - let font = self.get_regular(); + debug!("current font style:{:?}", self.style); + let font = self.get_by_style(self.style); let metrics = font.metrics(); ((metrics.ascent - metrics.descent) / metrics.units_per_em as f32 * self.size).ceil() as u32 } @@ -182,11 +190,14 @@ impl Default for FontCollection { impl FontCollection { /// Create a FontCollection with several fonts. - pub fn new>(font_list: &[(S, f32)]) -> Result { + pub fn new>( + font_list: &[(S, f32)], + font_style: FontStyle, + ) -> Result { let mut fonts = vec![]; for (name, size) in font_list { let name = name.as_ref(); - match ImageFont::new(name, *size) { + match ImageFont::new(name, *size, font_style) { Ok(font) => fonts.push(font), Err(err) => eprintln!("[error] Error occurs when load font `{}`: {}", name, err), } @@ -331,8 +342,8 @@ impl FontCollection { } /// Get the width of the given text - pub fn get_text_len(&self, text: &str) -> u32 { - self.layout(text, REGULAR).1 + pub fn get_text_len(&self, text: &str, style: FontStyle) -> u32 { + self.layout(text, style).1 } /// Draw the text to a image @@ -350,7 +361,7 @@ impl FontCollection { I: GenericImage, ::Subpixel: ValueInto + Clamp, { - let metrics = self.0[0].get_regular().metrics(); + let metrics = self.0[0].get_by_style(style).metrics(); let offset = (metrics.descent / metrics.units_per_em as f32 * self.0[0].size).round() as i32; diff --git a/src/formatter.rs b/src/formatter.rs index 7a6c3f3..7805e12 100644 --- a/src/formatter.rs +++ b/src/formatter.rs @@ -43,6 +43,9 @@ pub struct ImageFormatter { /// font of english character, should be mono space font /// Default: Hack (builtin) font: FontCollection, + /// font of english character, should be mono space font + /// Default: Hack (builtin) + font_style: FontStyle, /// Highlight lines highlight_lines: Vec, /// Shadow adder @@ -61,6 +64,8 @@ pub struct ImageFormatterBuilder { line_number: bool, /// Font of english character, should be mono space font font: Vec<(S, f32)>, + /// FontStyle of font + font_style: FontStyle, /// Highlight lines highlight_lines: Vec, /// Whether show the window controls @@ -115,6 +120,12 @@ impl + Default> ImageFormatterBuilder { self } + /// Set the font + pub fn font_style(mut self, font_style: FontStyle) -> Self { + self.font_style = font_style; + self + } + /// Whether show the windows controls pub fn window_controls(mut self, show: bool) -> Self { self.window_controls = show; @@ -155,7 +166,7 @@ impl + Default> ImageFormatterBuilder { let font = if self.font.is_empty() { FontCollection::default() } else { - FontCollection::new(&self.font)? + FontCollection::new(&self.font, self.font_style)? }; let title_bar = self.window_controls || self.window_title.is_some(); @@ -177,6 +188,7 @@ impl + Default> ImageFormatterBuilder { shadow_adder: self.shadow_adder, tab_width: self.tab_width, font, + font_style: self.font_style, line_offset: self.line_offset, }) } @@ -215,14 +227,14 @@ impl ImageFormatter { self.code_pad + if self.line_number { let tmp = format!("{:>width$}", 0, width = self.line_number_chars as usize); - 2 * self.line_number_pad + self.font.get_text_len(&tmp) + 2 * self.line_number_pad + self.font.get_text_len(&tmp, self.font_style) } else { 0 } } /// create - fn create_drawables(&self, v: &[Vec<(Style, &str)>]) -> Drawable { + fn create_drawables(&self, v: &[Vec<(Style, &str)>], style: FontStyle) -> Drawable { // tab should be replaced to whitespace so that it can be rendered correctly let tab = " ".repeat(self.tab_width as usize); let mut drawables = vec![]; @@ -232,21 +244,23 @@ impl ImageFormatter { let height = self.get_line_y(i as u32); let mut width = self.get_left_pad(); - for (style, text) in tokens { + for (style_, text) in tokens { let text = text.trim_end_matches('\n').replace('\t', &tab); if text.is_empty() { continue; } - drawables.push(( - width, - height, - Some(style.foreground), - style.font_style.into(), - text.to_owned(), - )); + if style == style_.font_style.into() { + drawables.push(( + width, + height, + Some(style_.foreground), + style, + text.to_owned(), + )); + } - width += self.font.get_text_len(&text); + width += self.font.get_text_len(&text, self.font_style); max_width = max_width.max(width); } @@ -255,7 +269,7 @@ impl ImageFormatter { if self.window_title.is_some() { let title = self.window_title.as_ref().unwrap(); - let title_width = self.font.get_text_len(title); + let title_width = self.font.get_text_len(title, self.font_style); let ctrls_offset = if self.window_controls { self.window_controls_width + self.title_bar_pad @@ -268,7 +282,7 @@ impl ImageFormatter { ctrls_offset + self.title_bar_pad, self.title_bar_pad + ctrls_center - self.font.get_font_height() / 2, None, - FontStyle::BOLD, + style, title.to_string(), )); @@ -298,7 +312,7 @@ impl ImageFormatter { color, self.code_pad, self.get_line_y(i), - FontStyle::REGULAR, + self.font_style, &line_mumber, ); } @@ -322,7 +336,12 @@ impl ImageFormatter { } // TODO: use &T instead of &mut T ? - pub fn format(&mut self, v: &[Vec<(Style, &str)>], theme: &Theme) -> DynamicImage { + pub fn format( + &mut self, + v: &[Vec<(Style, &str)>], + theme: &Theme, + style: FontStyle, + ) -> DynamicImage { if self.line_number { self.line_number_chars = (((v.len() + self.line_offset as usize) as f32).log10() + 1.0).floor() as u32; @@ -331,7 +350,7 @@ impl ImageFormatter { self.line_number_pad = 0; } - let drawables = self.create_drawables(v); + let drawables = self.create_drawables(v, style); let size = self.get_image_size(drawables.max_width, drawables.max_lineno); diff --git a/src/lib.rs b/src/lib.rs index 67551d8..b172a3d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,6 +8,7 @@ //! use silicon::utils::ShadowAdder; //! use silicon::formatter::ImageFormatterBuilder; //! use silicon::assets::HighlightingAssets; +//! use silicon::font::FontStyle; //! //! let ha = HighlightingAssets::new(); //! let (ps, ts) = (ha.syntax_set, ha.theme_set); @@ -30,7 +31,7 @@ //! .shadow_adder(ShadowAdder::default()) //! .build() //! .unwrap(); -//! let image = formatter.format(&highlight, theme); +//! let image = formatter.format(&highlight, theme,FontStyle::REGULAR); //! //! image.save("hello.png").unwrap(); //! ```