mirror of https://github.com/Aloxaf/silicon
feat: add --font-style arg
This commit is contained in:
parent
4d15f35dee
commit
6d0be00819
|
@ -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)
|
||||
|
|
|
@ -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)?;
|
||||
|
|
33
src/font.rs
33
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<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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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();
|
||||
//! ```
|
||||
|
|
Loading…
Reference in New Issue