feat: add --font-style arg

This commit is contained in:
iujakchu 2022-11-20 15:20:01 +08:00
parent 4d15f35dee
commit 6d0be00819
5 changed files with 78 additions and 30 deletions

View File

@ -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<Vec<u32>, 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<FontList>,
/// 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<Lines>,
@ -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)

View File

@ -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)?;

View File

@ -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<FontStyle, Font>,
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<Self, FontError> {
pub fn new(name: &str, size: f32, style: FontStyle) -> Result<Self, FontError> {
// 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<S: AsRef<str>>(font_list: &[(S, f32)]) -> Result<Self, FontError> {
pub fn new<S: AsRef<str>>(
font_list: &[(S, f32)],
font_style: FontStyle,
) -> Result<Self, FontError> {
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,
<I::Pixel as Pixel>::Subpixel: ValueInto<f32> + Clamp<f32>,
{
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;

View File

@ -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<u32>,
/// Shadow adder
@ -61,6 +64,8 @@ pub struct ImageFormatterBuilder<S> {
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<u32>,
/// Whether show the window controls
@ -115,6 +120,12 @@ impl<S: AsRef<str> + Default> ImageFormatterBuilder<S> {
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<S: AsRef<str> + Default> ImageFormatterBuilder<S> {
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<S: AsRef<str> + Default> ImageFormatterBuilder<S> {
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);

View File

@ -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();
//! ```