Browse Source

Update to more recent font-kit

Update to latest font-kit.
Make the PNG generator more generic so it can be used for other fonts.
Does not seem to work correctly with original ProFontIIx TTF file, but works as
expected with other TTF.
pr/update_fontkit
Marc Poulhiès 2 years ago
parent
commit
84617279b4
  1. 6
      Cargo.toml
  2. 19
      data/Makefile
  3. 145
      src/main.rs

6
Cargo.toml

@ -16,10 +16,12 @@ categories = ["rendering", "embedded", "no-std"]
[dependencies]
embedded-graphics = "0.6"
font-kit = { version = "0.1", optional = true }
font-kit = { version = "0.10", optional = true }
euclid = { version = "0.19", optional = true }
clap = { version = "2.33.3", optional = true }
image = { version = "0.20", optional = true, default-features = false, features = ["png_codec"] }
pathfinder_geometry = { version = "0.5.1", optional = true }
[features]
default = []
exe = ["font-kit", "euclid", "image"]
exe = ["font-kit", "euclid", "clap", "image", "pathfinder_geometry" ]

19
data/Makefile

@ -3,14 +3,19 @@ RAW = ${PNG:.png=.raw}
all: ${RAW}
TTFFONT:=ProFontIIx.ttf
FONTPREFIX:=ProFont
CARGO_ARGS:=run --release --features=exe -- $(TTFFONT) $(FONTPREFIX)
pngs:
cargo run --release -- 7
cargo run --release -- 9
cargo run --release -- 10
cargo run --release -- 12
cargo run --release -- 14
cargo run --release -- 18
cargo run --release -- 24
cargo $(CARGO_ARGS) 7
cargo $(CARGO_ARGS) 9
cargo $(CARGO_ARGS) 10
cargo $(CARGO_ARGS) 12
cargo $(CARGO_ARGS) 14
cargo $(CARGO_ARGS) 18
cargo $(CARGO_ARGS) 24
.SUFFIXES: .png .raw

145
src/main.rs

@ -1,45 +1,78 @@
#[cfg(feature = "exe")]
fn main () {
fn main() {
exe::main();
}
#[cfg(not(feature = "exe"))]
fn main() {
println!("profont must be compiled with the exe feature to enable the binary.");
println!("Must be compiled with the exe feature to enable the binary.");
}
#[cfg(feature = "exe")]
mod exe {
extern crate clap;
extern crate euclid;
extern crate font_kit;
extern crate image;
extern crate pathfinder_geometry;
use std::fs::File;
use self::euclid::{Point2D, Rect, Size2D};
use self::clap::{App, Arg, ArgMatches};
use self::euclid::Size2D;
use self::font_kit::canvas::{Canvas, Format, RasterizationOptions};
use self::font_kit::font::Font;
use self::font_kit::hinting::HintingOptions;
use self::image::Luma;
use self::pathfinder_geometry::{rect::RectI, transform2d::Transform2F};
use std::char;
use std::env;
use std::sync::Arc;
struct Glyph {
id: u32,
raster_rect: Rect<i32>,
raster_rect: RectI,
c: char,
}
fn get_args() -> ArgMatches<'static> {
let dumpglyph_arg = Arg::with_name("dumpglyph")
.help("Dump glyph in terminal")
.short("d")
.long("dumpglyph");
let ttf_filename_arg = Arg::with_name("TTF")
.help("TTF file name")
.required(true)
.index(1);
let png_prefix_arg = Arg::with_name("PNG-PREFIX")
.help("Prefix for PNG")
.required(true)
.index(2);
let size_arg = Arg::with_name("SIZE")
.help("Font size in blocks")
.default_value("32")
.index(3);
App::new("create-font")
.version("0.1")
.about("Simple tool to create image from TTF for creating font for embedded-graphics`")
.arg(ttf_filename_arg)
.arg(dumpglyph_arg)
.arg(png_prefix_arg)
.arg(size_arg)
.get_matches()
}
pub fn main() {
let font_data = include_bytes!("../data/ProFontIIx.ttf").to_vec();
let font = Font::from_bytes(Arc::new(font_data), 0).expect("error loading font");
let matches = get_args();
let ttf_filename = matches.value_of("TTF").unwrap();
let png_prefix = matches.value_of("PNG-PREFIX").unwrap();
let font_size: f32 = matches.value_of("SIZE").unwrap().parse().unwrap();
let dump_glyph_enable = matches.is_present("dumpglyph");
let font_size: f32 = env::args()
.nth(1)
.expect("font size argument must be supplied on command line")
.parse()
.expect("invalid font size");
let mut file = File::open(ttf_filename).unwrap();
let metrics = font.metrics();
let offset = (metrics.descent as f32 / metrics.units_per_em as f32 * font_size).round() as i32;
let font = Font::from_file(&mut file, 0).expect("error loading font");
// Print latin 1 characters
let basic = (' ' as u32..='~' as u32)
@ -59,16 +92,19 @@ mod exe {
.raster_bounds(
glyph_id,
font_size,
&Point2D::zero(),
Transform2F::default(),
HintingOptions::None,
RasterizationOptions::Bilevel,
).expect("unable to get raster bounds");
)
.expect("unable to get raster bounds");
Glyph {
id: glyph_id,
raster_rect,
c: chr,
}
})
}).collect();
})
.collect();
// Work out how big the glyphs are
let char_size = Size2D::new(
@ -77,41 +113,50 @@ mod exe {
.map(|glyph| {
glyph
.as_ref()
.map(|glyph| glyph.raster_rect.size.width)
.map(|glyph| glyph.raster_rect.width())
.unwrap_or(0)
}).max()
})
.max()
.unwrap(),
glyphs
.iter()
.map(|glyph| {
glyph
.as_ref()
.map(|glyph| glyph.raster_rect.size.height)
.map(|glyph| glyph.raster_rect.height())
.unwrap_or(0)
}).max()
})
.max()
.unwrap(),
).to_u32();
)
.to_u32();
println!("Size {:?}", char_size);
// Render the glyphs
let row_size = 32;
let img_size = Size2D::new(
char_size.width * row_size,
(char_size.height as f64 * glyphs.len() as f64 / row_size as f64).ceil() as u32,
char_size.height * ((glyphs.len() as u32 + row_size - 1) / row_size),
);
let mut imgbuf = image::GrayImage::new(img_size.width, img_size.height);
for (i, (_chr, glyph)) in all_chars.iter().zip(glyphs.iter()).enumerate() {
if let Some(glyph) = glyph {
let mut canvas = Canvas::new(&glyph.raster_rect.size.to_u32(), Format::A8);
let mut canvas = Canvas::new(glyph.raster_rect.size(), Format::A8);
font.rasterize_glyph(
&mut canvas,
glyph.id,
font_size,
&glyph.raster_rect.origin.to_f32(),
Transform2F::from_translation(-glyph.raster_rect.origin().to_f32()),
HintingOptions::None,
RasterizationOptions::Bilevel,
).expect("error rasterizing glyph");
)
.expect("error rasterizing glyph");
if dump_glyph_enable {
dump_glyph(glyph, &canvas);
}
let col = i as u32 % row_size;
let row = i as u32 / row_size;
@ -119,20 +164,17 @@ mod exe {
let img_y = row * char_size.height + char_size.height;
// Copy onto image
for y in (0u32..glyph.raster_rect.size.height as u32)
.into_iter()
.rev()
{
for y in (0..glyph.raster_rect.height()).into_iter().rev() {
let (row_start, row_end) =
(y as usize * canvas.stride, (y + 1) as usize * canvas.stride);
let row = &canvas.pixels[row_start..row_end];
for x in 0u32..glyph.raster_rect.size.width as u32 {
for x in 0..glyph.raster_rect.width() {
let val = row[x as usize];
if val != 0 {
let pixel_x = img_x as i32 + x as i32 + glyph.raster_rect.origin.x;
let pixel_y = img_y as i32 - glyph.raster_rect.size.height + y as i32
- glyph.raster_rect.origin.y
+ offset;
let pixel_x = img_x as i32 + x as i32 + glyph.raster_rect.origin().x();
let pixel_y = img_y as i32 - glyph.raster_rect.height() + y as i32;
if pixel_x >= 0 && pixel_y >= 0 {
imgbuf.put_pixel(pixel_x as u32, pixel_y as u32, Luma([0xFFu8]));
}
@ -142,8 +184,35 @@ mod exe {
}
}
let filename = format!("ProFont{}Point.png", font_size);
let filename = format!("{}{}Point.png", png_prefix, font_size);
imgbuf.save(&filename).expect("error saving PNG");
println!("Wrote {} with character size of {}", filename, char_size);
}
fn shade(value: u8) -> char {
match value {
0 => ' ',
1..=84 => '░',
85..=169 => '▒',
170..=254 => '▓',
_ => '█',
}
}
fn dump_glyph(glyph: &Glyph, canvas: &Canvas) {
println!("char '{}'", glyph.c);
for y in 0..glyph.raster_rect.height() {
let mut line = String::new();
let (row_start, row_end) =
(y as usize * canvas.stride, (y + 1) as usize * canvas.stride);
let row = &canvas.pixels[row_start..row_end];
for x in 0..glyph.raster_rect.width() {
let shade = shade(row[x as usize]);
line.push(shade);
line.push(shade);
}
println!("{}", line);
}
}
}

Loading…
Cancel
Save