Skip to content

Commit

Permalink
some updates
Browse files Browse the repository at this point in the history
  • Loading branch information
jaytaph committed Jan 6, 2025
1 parent 5ce9665 commit dedb8e9
Show file tree
Hide file tree
Showing 9 changed files with 187 additions and 148 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/linux.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,5 @@ jobs:
- name: Run the application
run: |
./target/release/fontmanager
./target/release/display-fonts
shell: bash
3 changes: 1 addition & 2 deletions .github/workflows/macos.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,5 @@ jobs:
- name: Run the application
run: |
./target/release/fontmanager
./target/release/display-fonts
shell: bash
if: ${{ matrix.os != 'windows-latest' }}
2 changes: 1 addition & 1 deletion .github/workflows/windows.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,5 @@ jobs:
- name: Run the application (Windows)
run: |
.\target\release\fontmanager.exe
.\target\release\display-fonts.exe
shell: cmd
9 changes: 9 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,15 @@ name = "fontmanager"
version = "0.1.0"
edition = "2021"

[[bin]]
name = "display-fonts"
path = "src/bin/display-fonts.rs"

[[bin]]
name = "generate-svg"
path = "src/bin/generate-svg.rs"


[dependencies]
colog = "1.3.0"
font-kit = "0.14.2"
Expand Down
34 changes: 34 additions & 0 deletions src/bin/display-fonts.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
use prettytable::{Attr, Cell, Row, Table};
use fontmanager::font_manager::FontManager;

fn main() {
colog::init();

let manager = FontManager::new();

let mut table = Table::new();
table.set_format(*prettytable::format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
table.set_titles(Row::new(vec![
Cell::new("Family").with_style(Attr::Bold),
Cell::new("Style").with_style(Attr::Bold),
Cell::new("Weight").with_style(Attr::Bold),
Cell::new("Stretch").with_style(Attr::Bold),
Cell::new("Monospaced").with_style(Attr::Bold),
Cell::new("Path").with_style(Attr::Bold),
Cell::new("Index").with_style(Attr::Bold),
]));

for info in manager.available_fonts() {
table.add_row(Row::new(vec![
Cell::new(&info.family),
Cell::new(&format!("{}", &info.style)),
Cell::new(&info.weight.to_string()),
Cell::new(&info.stretch.to_string()),
Cell::new(&info.monospaced.to_string()),
Cell::new(&info.path.to_str().unwrap()),
Cell::new(&info.index.unwrap_or(0).to_string()),
]));
}

table.printstd();
}
108 changes: 108 additions & 0 deletions src/bin/generate-svg.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
use freetype::Face;
use fontmanager::font_manager::{FontManager, FontStyle};

// const TEST_STRING1: &str = "A B C D E\n \u{EA84} a b c d e";

const TEST_STRING: &str = r"A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
a b c d e f g h i j k l m n o p q r s t u v w x y z
0 1 2 3 4 5 6 7 8 9 ( ) $ % @ & ¢ € [ \ ] ^ _ ` { | } ~ < > # = + - * / : ; , . ! ?
¡ ¿ ˆ ˜ ¨ ´ ` ˘ ˙ ˚ ˝ ˛ ˇ ˆ ˇ ˘ ˙ ˚ ˛ ˜ ˝ ˇ ˘ ˙ ˚ ˛ ˜ ˝ ˇ ˘ ˙ ˚ ˛ ˜ ˝
À Á Â Ã Ä Å Æ Ç È É Ê Ë Ì Í Î Ï Ð Ñ Ò Ó Ô Õ Ö Ø Ù Ú Û Ü Ý Þ ß
à á â ã ä å æ ç è é ê ë ì í î ï ð ñ ò ó ô õ ö ø ù ú û ü ý þ ÿ
Ā ā Ă ă Ą ą Ć ć Ĉ ĉ Ċ ċ Č č Ď ď Đ đ Ē ē Ĕ ĕ Ė ė Ę ę Ě ě Ĝ ĝ Ğ ğ
Ġ ġ Ģ ģ Ĥ ĥ Ħ ħ Ĩ ĩ Ī ī Ĭ ĭ Į į İ ı IJ ij Ĵ ĵ Ķ ķ ĸ Ĺ ĺ Ļ ļ Ľ ľ
Ŀ ŀ Ł ł Ń ń Ņ ņ Ň ň ʼn Ŋ ŋ Ō ō Ŏ ŏ Ő ő Œ œ Ŕ ŕ Ŗ ŗ Ř ř Ś ś Ŝ ŝ
Ş ş Š š Ţ ţ Ť ť Ŧ ŧ Ũ ũ Ū ū Ŭ ŭ Ů ů Ű ű Ų ų Ŵ ŵ Ŷ ŷ Ÿ Ź ź Ż ż Ž ž
ſ ƀ Ɓ Ƃ ƃ Ƅ ƅ Ɔ Ƈ ƈ Ɖ Ɗ Ƌ ƌ ƍ Ǝ Ə Ɛ Ƒ ƒ Ɠ Ɣ ƕ Ɩ Ɨ Ƙ ƙ ƚ ƛ Ɯ Ɲ ƞ Ɵ
Ơ ơ Ƣ ƣ Ƥ ƥ Ʀ Ƨ ƨ Ʃ ƪ ƫ Ƭ ƭ Ʈ Ư ư Ʊ Ʋ Ƴ ƴ Ƶ ƶ Ʒ Ƹ ƹ ƺ ƻ Ƽ ƽ ƾ ƿ
ǀ ǁ ǂ ǃ DŽ Dž dž LJ Lj lj NJ Nj nj Ǎ ǎ Ǐ ǐ Ǒ ǒ Ǔ ǔ Ǖ ǖ Ǘ ǘ Ǚ ǚ Ǜ ǜ ǝ
Ǟ ǟ Ǡ ǡ Ǣ ǣ Ǥ ǥ Ǧ ǧ Ǩ ǩ Ǫ ǫ Ǭ ǭ Ǯ ǯ ǰ DZ Dz dz Ǵ ǵ Ƕ Ƿ Ǹ ǹ Ǻ ǻ
Ǽ ǽ Ǿ ǿ Ȁ ȁ Ȃ ȃ Ȅ ȅ Ȇ ȇ Ȉ ȉ Ȋ ȋ Ȍ ȍ Ȏ ȏ Ȑ ȑ Ȓ ȓ Ȕ ȕ Ȗ ȗ Ș ș Ț ț
Ȝ ȝ Ȟ ȟ Ƞ ȡ Ȣ ȣ Ȥ ȥ Ȧ ȧ Ȩ ȩ Ȫ ȫ Ȭ ȭ Ȯ ȯ Ȱ ȱ Ȳ ȳ ȴ ȵ ȶ ȷ ȸ ȹ Ⱥ
Ȼ ȼ Ƚ Ⱦ ȿ ɀ Ɂ ɂ Ƀ Ʉ Ʌ Ɇ ɇ Ɉ ɉ Ɋ ɋ Ɍ ɍ Ɏ ɏ ɐ ɑ ɒ ɓ ɔ ɕ ɖ ɗ ɘ ə ɚ
ɜ ɝ ɞ ɟ ɠ ɡ ɢ ɣ ɤ ɥ ɦ ɧ ɨ ɩ ɪ ɫ ɬ ɭ ɮ ɯ ɰ ɱ ɲ ɳ ɴ ɵ ɶ ɷ ɸ ɹ
Hello world from the Gosub FontManager system!
";

fn main() {
colog::init();

let manager = FontManager::new();

let info = manager.find(vec!["Nimbus Sans Narrow"], FontStyle::Normal);
let face = manager.load(info.unwrap()).unwrap();
char_to_svg(face, TEST_STRING);
}

fn char_to_svg(face: Face, content: &str) {
face.set_char_size(10 * 64, 0, 10, 0).unwrap();

println!("<?xml version=\"1.0\" standalone=\"no\"?>");
println!("<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"");
println!("\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">");
println!("<svg viewBox=\"0 0 100 200\" xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">");

let mut x_pos = -155.0;
let mut y_pos = 10.0;

for c in content.chars() {
if c == '\n' {
x_pos = -155.0;
y_pos += 10.0;
continue;
}

if c == ' ' {
x_pos += 2.5;
continue;
}

face.load_char(c as usize, freetype::face::LoadFlag::NO_SCALE).unwrap();

let glyph = face.glyph();
let metrics = glyph.metrics();
// let xmin = metrics.horiBearingX - 5;
let width = metrics.width + 10;
// let ymin = -metrics.horiBearingY - 5;
// let height = metrics.height + 10;
// let scale_factor = 10.0 / width as f32;
let scale_factor = 0.0056;

let outline = glyph.outline().unwrap();

for contour in outline.contours_iter() {
let start = contour.start();

// dbg!(x_pos, y_pos);
println!("<g transform=\"translate({}, {}) scale({})\">", (x_pos - 1.0), (y_pos - 1.0), scale_factor);

println!(
"<path fill=\"none\" stroke=\"black\" stroke-width=\"16\" d=\"M {} {}",
start.x, -start.y
);
for curve in contour {
draw_curve(curve);
}
println!("Z \" />");
println!("</g>");
}

x_pos += width as f32 * scale_factor;
x_pos += 1.0;
}
println!("</svg>");
}


fn draw_curve(curve: freetype::outline::Curve) {
match curve {
freetype::outline::Curve::Line(pt) => println!("L {} {}", pt.x, -pt.y),
freetype::outline::Curve::Bezier2(pt1, pt2) => {
println!("Q {} {} {} {}", pt1.x, -pt1.y, pt2.x, -pt2.y)
}
freetype::outline::Curve::Bezier3(pt1, pt2, pt3) => println!(
"C {} {} {} {} {} {}",
pt1.x, -pt1.y, pt2.x, -pt2.y, pt3.x, -pt3.y
),
}
}
111 changes: 32 additions & 79 deletions src/main.rs → src/font_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@ use font_kit::properties::Properties;
use font_kit::source::SystemSource;
use freetype::{Face, Library};
use log::info;
use prettytable::{Attr, Cell, Row, Table};

#[allow(dead_code)]
const LOG_TARGET: &str = "font-manager";

#[derive(Clone, Debug)]
enum FontStyle {
pub enum FontStyle {
Normal,
Italic,
Oblique,
Expand All @@ -29,28 +31,37 @@ impl Display for FontStyle {
}

#[derive(Clone, Debug)]
struct FontInfo {
pub struct FontInfo {
/// Family name of the font (e.g. "Arial")
pub family: String,
/// Style of the font
pub style: FontStyle,
/// Weight (400 normal, 700 bold)
pub weight: f32,
/// Stretch (1.0 normal, < 1.0 condensed)
pub stretch: f32,
/// Font is monospaced
pub monospaced: bool,

/// Path to the font file
pub path: PathBuf,
/// Index of the face in the font-file
pub index: Option<i32>,
}

struct FontManager {
#[allow(unused)]
pub struct FontManager {
source: SystemSource,
ft_library: Library,
info: Vec<FontInfo>,
/// Vec of all font-info structures found
font_info: Vec<FontInfo>,
/// List of all font handles
handles: Vec<Handle>,
/// Cache of font faces that are loaded through freetype
face_cache: Arc<Mutex<HashMap<String, Face>>>,
}

impl FontManager {
fn new() -> Self {
pub fn new() -> Self {
let library = Library::init().expect("unable to init freetype library");

let source = SystemSource::new();
Expand All @@ -66,12 +77,21 @@ impl FontManager {
FontManager {
source,
ft_library: library,
info: font_info,
font_info: font_info,
handles,
face_cache: Arc::new(Mutex::new(HashMap::new())),
}
}

/// Returns all available fonts
pub fn available_fonts(&self) -> Vec<FontInfo> {
let mut fonts = self.font_info.clone();
fonts.sort_by_key(|fi| fi.family.clone());

fonts
}

/// Returns the first font that matches the given family and style
pub fn find(&self, families: Vec<&str>, style: FontStyle) -> Option<FontInfo> {
let mut f = Vec::new();
for family in families {
Expand Down Expand Up @@ -100,10 +120,11 @@ impl FontManager {
}
}

/// Load the font face for the given font info
pub fn load(&self, font_info: FontInfo) -> Result<Face, anyhow::Error> {
let cache_key = format!("{}:{}", font_info.family, font_info.style);
if let Some(font_face) = self.face_cache.lock().unwrap().get(&cache_key) {
info!(target: "font", "Font loaded from cache: {}", cache_key);
info!(target: LOG_TARGET, "Font loaded from cache: {}", cache_key);
// @todo: Can we somehow return the face within the cache so we don't need to copy it?
return Ok(font_face.clone());
}
Expand All @@ -116,15 +137,14 @@ impl FontManager {
}
};


info!(target: "font",
info!(target: LOG_TARGET,
"Font loaded: {} (number of glyphs: {})",
face.family_name().unwrap_or("Unknown".parse()?),
face.num_glyphs()
);

// @todo: same here.. we use a clone to store into cache, but can we just use the data we loaded through freetype?
info!(target: "font", "Caching font face: {}", cache_key);
info!(target: LOG_TARGET, "Caching font face: {}", cache_key);
self.face_cache.lock().unwrap().insert(cache_key.clone(), face.clone());
Ok(face)
}
Expand Down Expand Up @@ -159,70 +179,3 @@ impl FontManager {
}
}
}

const TEST_STRING: &str = r"A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
a b c d e f g h i j k l m n o p q r s t u v w x y z
0 1 2 3 4 5 6 7 8 9 ( ) $ % @ & ¢ € [ \ ] ^ _ ` { | } ~ < > # = + - * / : ; , . ! ?
¡ ¿ ˆ ˜ ¨ ´ ` ˘ ˙ ˚ ˝ ˛ ˇ ˆ ˇ ˘ ˙ ˚ ˛ ˜ ˝ ˇ ˘ ˙ ˚ ˛ ˜ ˝ ˇ ˘ ˙ ˚ ˛ ˜ ˝
À Á Â Ã Ä Å Æ Ç È É Ê Ë Ì Í Î Ï Ð Ñ Ò Ó Ô Õ Ö Ø Ù Ú Û Ü Ý Þ ß
à á â ã ä å æ ç è é ê ë ì í î ï ð ñ ò ó ô õ ö ø ù ú û ü ý þ ÿ
Ā ā Ă ă Ą ą Ć ć Ĉ ĉ Ċ ċ Č č Ď ď Đ đ Ē ē Ĕ ĕ Ė ė Ę ę Ě ě Ĝ ĝ Ğ ğ
Ġ ġ Ģ ģ Ĥ ĥ Ħ ħ Ĩ ĩ Ī ī Ĭ ĭ Į į İ ı IJ ij Ĵ ĵ Ķ ķ ĸ Ĺ ĺ Ļ ļ Ľ ľ
Ŀ ŀ Ł ł Ń ń Ņ ņ Ň ň ʼn Ŋ ŋ Ō ō Ŏ ŏ Ő ő Œ œ Ŕ ŕ Ŗ ŗ Ř ř Ś ś Ŝ ŝ
Ş ş Š š Ţ ţ Ť ť Ŧ ŧ Ũ ũ Ū ū Ŭ ŭ Ů ů Ű ű Ų ų Ŵ ŵ Ŷ ŷ Ÿ Ź ź Ż ż Ž ž
ſ ƀ Ɓ Ƃ ƃ Ƅ ƅ Ɔ Ƈ ƈ Ɖ Ɗ Ƌ ƌ ƍ Ǝ Ə Ɛ Ƒ ƒ Ɠ Ɣ ƕ Ɩ Ɨ Ƙ ƙ ƚ ƛ Ɯ Ɲ ƞ Ɵ
Ơ ơ Ƣ ƣ Ƥ ƥ Ʀ Ƨ ƨ Ʃ ƪ ƫ Ƭ ƭ Ʈ Ư ư Ʊ Ʋ Ƴ ƴ Ƶ ƶ Ʒ Ƹ ƹ ƺ ƻ Ƽ ƽ ƾ ƿ
ǀ ǁ ǂ ǃ DŽ Dž dž LJ Lj lj NJ Nj nj Ǎ ǎ Ǐ ǐ Ǒ ǒ Ǔ ǔ Ǖ ǖ Ǘ ǘ Ǚ ǚ Ǜ ǜ ǝ
Ǟ ǟ Ǡ ǡ Ǣ ǣ Ǥ ǥ Ǧ ǧ Ǩ ǩ Ǫ ǫ Ǭ ǭ Ǯ ǯ ǰ DZ Dz dz Ǵ ǵ Ƕ Ƿ Ǹ ǹ Ǻ ǻ
Ǽ ǽ Ǿ ǿ Ȁ ȁ Ȃ ȃ Ȅ ȅ Ȇ ȇ Ȉ ȉ Ȋ ȋ Ȍ ȍ Ȏ ȏ Ȑ ȑ Ȓ ȓ Ȕ ȕ Ȗ ȗ Ș ș Ț ț
Ȝ ȝ Ȟ ȟ Ƞ ȡ Ȣ ȣ Ȥ ȥ Ȧ ȧ Ȩ ȩ Ȫ ȫ Ȭ ȭ Ȯ ȯ Ȱ ȱ Ȳ ȳ ȴ ȵ ȶ ȷ ȸ ȹ Ⱥ
Ȼ ȼ Ƚ Ⱦ ȿ ɀ Ɂ ɂ Ƀ Ʉ Ʌ Ɇ ɇ Ɉ ɉ Ɋ ɋ Ɍ ɍ Ɏ ɏ ɐ ɑ ɒ ɓ ɔ ɕ ɖ ɗ ɘ ə ɚ
ɛ ɜ ɝ ɞ ɟ ɠ ɡ ɢ ɣ ɤ ɥ ɦ ɧ ɨ ɩ ɪ ɫ ɬ ɭ ɮ ɯ ɰ ɱ ɲ ɳ ɴ ɵ ɶ ɷ ɸ ɹ
Hello world from the Gosub FontManager system!
";

fn main() {
colog::init();

let manager = FontManager::new();

print_font_table(&manager);

let info = manager.find(vec!["arial"], FontStyle::Normal);
dbg!(&info);
let info = manager.find(vec!["serif"], FontStyle::Normal);
dbg!(&info);
let info = manager.find(vec!["monospace"], FontStyle::Normal);
dbg!(&info);

char_to_svg(&manager, "Arial", "Regular", '');
}

fn print_font_table(manager: &FontManager) {
let mut table = Table::new();
table.set_format(*prettytable::format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
table.set_titles(Row::new(vec![
Cell::new("PostScript Name").with_style(Attr::Bold),
Cell::new("Name").with_style(Attr::Bold),
Cell::new("Family").with_style(Attr::Bold),
Cell::new("Path").with_style(Attr::Bold),
Cell::new("Monospaced").with_style(Attr::Bold),
Cell::new("Style").with_style(Attr::Bold),
Cell::new("Weight").with_style(Attr::Bold),
Cell::new("Stretch").with_style(Attr::Bold),
]));

for info in manager.info.iter() {
table.add_row(Row::new(vec![
Cell::new(&info.family),
Cell::new(&format!("{}", &info.style)),
Cell::new(&info.weight.to_string()),
Cell::new(&info.stretch.to_string()),
Cell::new(&info.monospaced.to_string()),
Cell::new(&info.path.to_str().unwrap()),
Cell::new(&info.index.unwrap_or(0).to_string()),
]));
}

table.printstd();
}
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod font_manager;
Loading

0 comments on commit dedb8e9

Please sign in to comment.