initial commit
Signed-off-by: Sean Cross <sean@xobs.io>
This commit is contained in:
commit
4922d63c8c
12
Cargo.toml
Normal file
12
Cargo.toml
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
[package]
|
||||||
|
name = "rust-font-test"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Sean Cross <sean@xobs.io>"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
fontdue = "0.0.1"
|
||||||
|
stats_alloc = "0.1.8"
|
||||||
|
rusttype = "0.8.1"
|
BIN
resources/NotoSans-Regular.ttf
Normal file
BIN
resources/NotoSans-Regular.ttf
Normal file
Binary file not shown.
BIN
resources/WenQuanYiMicroHei-01.ttf
Normal file
BIN
resources/WenQuanYiMicroHei-01.ttf
Normal file
Binary file not shown.
115
src/main.rs
Normal file
115
src/main.rs
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
use rusttype::{point, FontCollection, PositionedGlyph, Scale};
|
||||||
|
use std::io::Write;
|
||||||
|
|
||||||
|
// Add an allocator that lets us keep track of the system
|
||||||
|
extern crate stats_alloc;
|
||||||
|
use stats_alloc::{StatsAlloc, Region, INSTRUMENTED_SYSTEM};
|
||||||
|
use std::alloc::System;
|
||||||
|
#[global_allocator]
|
||||||
|
static GLOBAL: &StatsAlloc<System> = &INSTRUMENTED_SYSTEM;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let font = include_bytes!("../resources/WenQuanYiMicroHei-01.ttf");
|
||||||
|
test_rusttype(font);
|
||||||
|
test_fontdue(font);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_fontdue(font: &[u8]) {
|
||||||
|
let reg = Region::new(&GLOBAL);
|
||||||
|
println!("start: {:#?}", reg.change());
|
||||||
|
|
||||||
|
// Parse it into the font type.
|
||||||
|
let mut font = fontdue::Font::from_bytes(font).unwrap();
|
||||||
|
println!("from_bytes: {:#?}", reg.change());
|
||||||
|
|
||||||
|
// Rasterize and get the layout metrics for the letter 'g' at 17px.
|
||||||
|
let (metrics, bitmap) = font.rasterize('欢', 17.0);
|
||||||
|
println!("font.rasterize: {:#?}", reg.change());
|
||||||
|
|
||||||
|
// Used here to ensure that the value is not
|
||||||
|
// dropped before we check the statistics
|
||||||
|
::std::mem::size_of_val(&metrics);
|
||||||
|
::std::mem::size_of_val(&bitmap);
|
||||||
|
::std::mem::size_of_val(&font);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_rusttype(font: &[u8]) {
|
||||||
|
let reg = Region::new(&GLOBAL);
|
||||||
|
let collection = FontCollection::from_bytes(font).unwrap_or_else(|e| {
|
||||||
|
panic!("error constructing a FontCollection from bytes: {}", e);
|
||||||
|
});
|
||||||
|
println!("collection: {:#?}", reg.change());
|
||||||
|
let font = collection
|
||||||
|
.into_font() // only succeeds if collection consists of one font
|
||||||
|
.unwrap_or_else(|e| {
|
||||||
|
panic!("error turning FontCollection into a Font: {}", e);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Desired font pixel height
|
||||||
|
let height: f32 = 20.0; // to get 80 chars across (fits most terminals); adjust as desired
|
||||||
|
let pixel_height = height.ceil() as usize;
|
||||||
|
|
||||||
|
// 2x scale in x direction to counter the aspect ratio of monospace characters.
|
||||||
|
let scale = Scale {
|
||||||
|
x: height * 2.0,
|
||||||
|
y: height,
|
||||||
|
};
|
||||||
|
|
||||||
|
// The origin of a line of text is at the baseline (roughly where
|
||||||
|
// non-descending letters sit). We don't want to clip the text, so we shift
|
||||||
|
// it down with an offset when laying it out. v_metrics.ascent is the
|
||||||
|
// distance between the baseline and the highest edge of any glyph in
|
||||||
|
// the font. That's enough to guarantee that there's no clipping.
|
||||||
|
let v_metrics = font.v_metrics(scale);
|
||||||
|
let offset = point(0.0, v_metrics.ascent);
|
||||||
|
println!("metrics: {:#?}", reg.change());
|
||||||
|
|
||||||
|
// Glyphs to draw for "RustType". Feel free to try other strings.
|
||||||
|
let glyphs: Vec<PositionedGlyph<'_>> = font.layout("Xous:發啊你好", scale, offset).collect();
|
||||||
|
println!("glyphs: {:#?}", reg.change());
|
||||||
|
|
||||||
|
// Find the most visually pleasing width to display
|
||||||
|
let width = glyphs
|
||||||
|
.iter()
|
||||||
|
.rev()
|
||||||
|
.map(|g| g.position().x as f32 + g.unpositioned().h_metrics().advance_width)
|
||||||
|
.next()
|
||||||
|
.unwrap_or(0.0)
|
||||||
|
.ceil() as usize;
|
||||||
|
|
||||||
|
println!("width: {}, height: {}", width, pixel_height);
|
||||||
|
|
||||||
|
// Rasterise directly into ASCII art.
|
||||||
|
let mut pixel_data = vec![b'@'; width * pixel_height];
|
||||||
|
let mapping = b"@%#x+=:-. "; // The approximation of greyscale
|
||||||
|
let mapping_scale = (mapping.len() - 1) as f32;
|
||||||
|
for g in glyphs {
|
||||||
|
if let Some(bb) = g.pixel_bounding_box() {
|
||||||
|
g.draw(|x, y, v| {
|
||||||
|
// v should be in the range 0.0 to 1.0
|
||||||
|
let i = (v * mapping_scale + 0.5) as usize;
|
||||||
|
// so something's wrong if you get $ in the output.
|
||||||
|
let c = mapping.get(i).cloned().unwrap_or(b'$');
|
||||||
|
let x = x as i32 + bb.min.x;
|
||||||
|
let y = y as i32 + bb.min.y;
|
||||||
|
// There's still a possibility that the glyph clips the boundaries of the bitmap
|
||||||
|
if x >= 0 && x < width as i32 && y >= 0 && y < pixel_height as i32 {
|
||||||
|
let x = x as usize;
|
||||||
|
let y = y as usize;
|
||||||
|
pixel_data[(x + y * width)] = c;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
println!("rasterization: {:#?}", reg.change());
|
||||||
|
|
||||||
|
// Print it out
|
||||||
|
let stdout = ::std::io::stdout();
|
||||||
|
let mut handle = stdout.lock();
|
||||||
|
for j in 0..pixel_height {
|
||||||
|
handle
|
||||||
|
.write_all(&pixel_data[j * width..(j + 1) * width])
|
||||||
|
.unwrap();
|
||||||
|
handle.write_all(b"\n").unwrap();
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user