Browse Source

Merge pull request #1 from wezm/font-kit

Build images programatically
pr/update_fontkit
Wesley Moore 4 years ago committed by GitHub
parent
commit
1c7a81b4ca
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 18
      Cargo.toml
  2. 2
      LICENCE
  3. 15
      README.md
  4. BIN
      data/IMG_2198.jpg
  5. 11
      data/Makefile
  6. BIN
      data/ProFont10Point.png
  7. BIN
      data/ProFont10Point.raw
  8. BIN
      data/ProFont12Point.png
  9. BIN
      data/ProFont12Point.raw
  10. BIN
      data/ProFont14Point.png
  11. BIN
      data/ProFont14Point.raw
  12. BIN
      data/ProFont18Point.png
  13. BIN
      data/ProFont18Point.raw
  14. BIN
      data/ProFont24Point.png
  15. BIN
      data/ProFont24Point.raw
  16. BIN
      data/ProFont7Point.png
  17. BIN
      data/ProFont7Point.raw
  18. BIN
      data/ProFont9Point.png
  19. BIN
      data/ProFont9Point.raw
  20. BIN
      data/ProFontIIx.ttf
  21. 477
      src/lib.rs
  22. 149
      src/main.rs

18
Cargo.toml

@ -1,7 +1,25 @@
[package]
name = "profont"
description = "The ProFont monospace font for use with the embedded-graphics crate."
version = "0.1.0"
authors = ["Wesley Moore <wes@wezm.net>"]
documentation = "https://docs.rs/crate/profont"
repository = "https://github.com/wezm/profont"
readme = "README.md"
license = "MIT"
keywords = ["bitmap", "monochrome", "font", "monospace"]
categories = ["rendering", "embedded", "no-std"]
[dependencies]
embedded-graphics = "0.4"
font-kit = { version = "0.1", optional = true }
euclid = { version = "0.19", optional = true }
image = { version = "0.20", optional = true, default-features = false, features = ["png_codec"] }
[features]
default-features = []
exe = ["font-kit", "euclid", "image"]

2
LICENCE

@ -1,6 +1,6 @@
ProFont
MIT License
Copyright (c) 2018 Wesley Moore
Copyright (c) 2014 Tobias Jung, Carl Osterwald, Stephen C. Gilardi, Andrew Welch
Permission is hereby granted, free of charge, to any person obtaining a copy

15
README.md

@ -1,7 +1,13 @@
# ProFont
[The ProFont programming font](https://web.archive.org/web/20180412214402/http://tobiasjung.name/profont/)
for [embedded-graphics](https://github.com/jamwaffles/embedded-graphics).
[The ProFont monospace font][profont] for use with the [embedded-graphics] crate.
[![crates.io](https://img.shields.io/crates/v/profont.svg)](https://crates.io/crates/profont)
[![Documentation](https://docs.rs/profont/badge.svg)][crate-docs]
<img src="https://raw.githubusercontent.com/wezm/profont/master/IMG_2198.jpg" width="459" alt="Photo of ProFront on an ePaper display" />
`profont` is licensed under the [MIT license][MIT].
## Specimens
@ -34,3 +40,8 @@ ProFont is available in 7 sizes:
### 24 Point
![24 Point ProFont](https://raw.githubusercontent.com/wezm/profont/master/data/ProFont24Point.png)
[embedded-graphics]: https://github.com/jamwaffles/embedded-graphics
[profont]: https://web.archive.org/web/20180412214402/http://tobiasjung.name/profont/
[MIT]: https://github.com/wezm/profont/blob/master/LICENSE
[crate-docs]: https://docs.rs/crate/profont

BIN
data/IMG_2198.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 167 KiB

11
data/Makefile

@ -3,7 +3,18 @@ RAW = ${PNG:.png=.raw}
all: ${RAW}
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
.SUFFIXES: .png .raw
.png.raw:
convert ${.IMPSRC} -depth 1 gray:${.TARGET}
.PHONY: pngs

BIN
data/ProFont10Point.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

BIN
data/ProFont10Point.raw

Binary file not shown.

BIN
data/ProFont12Point.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

BIN
data/ProFont12Point.raw

Binary file not shown.

BIN
data/ProFont14Point.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

BIN
data/ProFont14Point.raw

Binary file not shown.

BIN
data/ProFont18Point.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 5.4 KiB

BIN
data/ProFont18Point.raw

Binary file not shown.

BIN
data/ProFont24Point.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

After

Width:  |  Height:  |  Size: 8.7 KiB

BIN
data/ProFont24Point.raw

Binary file not shown.

BIN
data/ProFont7Point.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
data/ProFont7Point.raw

Binary file not shown.

BIN
data/ProFont9Point.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

BIN
data/ProFont9Point.raw

Binary file not shown.

BIN
data/ProFontIIx.ttf

Binary file not shown.

477
src/lib.rs

@ -1,12 +1,50 @@
//! The ProFont programming font. Image data taken from the
//! [ProFont homepage](https://web.archive.org/web/20180412214402/http://tobiasjung.name/profont/)
//! The [ProFont](https://web.archive.org/web/20180412214402/http://tobiasjung.name/profont/)
//! monospace programming font for use with
//! [embedded-graphics](https://github.com/jamwaffles/embedded-graphics). Font data taken from the
//! [ProFont homepage](https://web.archive.org/web/20180412214402/http://tobiasjung.name/profont/).
//!
//! ### Synopsis
//!
//! Assuming `display` is something that implements the [Drawing
//! trait](https://docs.rs/embedded-graphics/0.4.4/embedded_graphics/trait.Drawing.html)
//!
//! ```no_run
//! display.draw(
//! ProFont24Point::render_str("Hello World")
//! .with_stroke(Some(Color::Red))
//! .with_fill(Some(Color::White))
//! .translate(Coord::new(10, 10))
//! .into_iter(),
//! );
//! ```
//!
//! For a more complete example see [the example in the ssd1675
//! crate](https://github.com/wezm/ssd1675/blob/master/examples/raspberry_pi_inky_phat.rs).
//!
//! ### Glyph Coverage
//!
//! This crate provides support for [ISO/IEC 8859-1](https://en.wikipedia.org/wiki/ISO/IEC_8859-1)
//! (latin1), although do note that the font is missing a few glyphs in this range.
extern crate embedded_graphics;
use embedded_graphics::fonts::font_builder::{FontBuilder, FontBuilderConf};
#[cfg(test)]
pub(crate) mod mock_display;
const CHARS_PER_ROW: u32 = 32;
fn char_offset_impl(c: char) -> u32 {
let fallback = '?' as u32 - ' ' as u32;
if c < ' ' {
return fallback;
}
if c <= '~' {
return c as u32 - ' ' as u32;
}
if c < '\u{00A0}' || c > 'ÿ' {
return fallback;
}
c as u32 - ' ' as u32 - 33
}
#[derive(Debug, Copy, Clone)]
pub enum ProFont7PointConf {}
@ -14,23 +52,13 @@ impl FontBuilderConf for ProFont7PointConf {
const FONT_IMAGE: &'static [u8] = include_bytes!("../data/ProFont7Point.raw");
const CHAR_HEIGHT: u32 = 9;
const CHAR_WIDTH: u32 = 5;
const FONT_IMAGE_WIDTH: u32 = 200;
const FONT_IMAGE_WIDTH: u32 = Self::CHAR_WIDTH * CHARS_PER_ROW;
fn char_offset(c: char) -> u32 {
let fallback = '?' as u32 - ' ' as u32;
if c < ' ' {
return fallback;
}
if c <= '~' {
return c as u32 - ' ' as u32;
}
if c < '¡' || c > 'ÿ' {
return fallback;
}
c as u32 - ' ' as u32 - 34
char_offset_impl(c)
}
}
/// The 7 point size.
/// The 7 point size with a character size of 5x9 pixels.
pub type ProFont7Point<'a, C> = FontBuilder<'a, C, ProFont7PointConf>;
#[derive(Debug, Copy, Clone)]
@ -39,391 +67,86 @@ impl FontBuilderConf for ProFont9PointConf {
const FONT_IMAGE: &'static [u8] = include_bytes!("../data/ProFont9Point.raw");
const CHAR_HEIGHT: u32 = 11;
const CHAR_WIDTH: u32 = 6;
const FONT_IMAGE_WIDTH: u32 = 240;
const FONT_IMAGE_WIDTH: u32 = Self::CHAR_WIDTH * CHARS_PER_ROW;
fn char_offset(c: char) -> u32 {
let fallback = '?' as u32 - ' ' as u32;
if c < ' ' {
return fallback;
}
if c <= '~' {
return c as u32 - ' ' as u32;
}
if c < '¡' || c > 'ÿ' {
return fallback;
}
c as u32 - ' ' as u32 - 34
char_offset_impl(c)
}
}
/// The 9 point size.
/// The 9 point size with a character size of 6x11 pixels.
pub type ProFont9Point<'a, C> = FontBuilder<'a, C, ProFont9PointConf>;
#[derive(Debug, Copy, Clone)]
pub enum ProFont10PointConf {}
impl FontBuilderConf for ProFont10PointConf {
const FONT_IMAGE: &'static [u8] = include_bytes!("../data/ProFont10Point.raw");
const CHAR_HEIGHT: u32 = 13;
const CHAR_WIDTH: u32 = 7;
const FONT_IMAGE_WIDTH: u32 = Self::CHAR_WIDTH * CHARS_PER_ROW;
fn char_offset(c: char) -> u32 {
char_offset_impl(c)
}
}
/// The 10 point size with a character size of 7x13 pixels.
pub type ProFont10Point<'a, C> = FontBuilder<'a, C, ProFont10PointConf>;
#[derive(Debug, Copy, Clone)]
pub enum ProFont12PointConf {}
impl FontBuilderConf for ProFont12PointConf {
const FONT_IMAGE: &'static [u8] = include_bytes!("../data/ProFont12Point.raw");
const CHAR_HEIGHT: u32 = 15;
const CHAR_WIDTH: u32 = 8;
const FONT_IMAGE_WIDTH: u32 = Self::CHAR_WIDTH * CHARS_PER_ROW;
fn char_offset(c: char) -> u32 {
char_offset_impl(c)
}
}
/// The 12 point size with a character size of 8x15 pixels.
pub type ProFont12Point<'a, C> = FontBuilder<'a, C, ProFont12PointConf>;
#[derive(Debug, Copy, Clone)]
pub enum ProFont14PointConf {}
impl FontBuilderConf for ProFont14PointConf {
const FONT_IMAGE: &'static [u8] = include_bytes!("../data/ProFont14Point.raw");
const CHAR_HEIGHT: u32 = 18;
const CHAR_WIDTH: u32 = 10;
const FONT_IMAGE_WIDTH: u32 = Self::CHAR_WIDTH * CHARS_PER_ROW;
fn char_offset(c: char) -> u32 {
char_offset_impl(c)
}
}
/// The 14 point size with a character size of 10x18 pixels.
pub type ProFont14Point<'a, C> = FontBuilder<'a, C, ProFont14PointConf>;
#[derive(Debug, Copy, Clone)]
pub enum ProFont18PointConf {}
impl FontBuilderConf for ProFont18PointConf {
const FONT_IMAGE: &'static [u8] = include_bytes!("../data/ProFont18Point.raw");
const CHAR_HEIGHT: u32 = 22;
const CHAR_WIDTH: u32 = 12;
const FONT_IMAGE_WIDTH: u32 = 480;
const FONT_IMAGE_WIDTH: u32 = Self::CHAR_WIDTH * CHARS_PER_ROW;
fn char_offset(c: char) -> u32 {
let fallback = '?' as u32 - ' ' as u32;
if c < ' ' {
return fallback;
}
if c <= '~' {
return c as u32 - ' ' as u32;
}
if c < '¡' || c > 'ÿ' {
return fallback;
}
c as u32 - ' ' as u32 - 34
char_offset_impl(c)
}
}
/// The 18 point size.
/// The 18 point size with a character size of 12x22 pixels.
pub type ProFont18Point<'a, C> = FontBuilder<'a, C, ProFont18PointConf>;
#[derive(Debug, Copy, Clone)]
pub enum ProFont24PointConf {}
impl FontBuilderConf for ProFont24PointConf {
const FONT_IMAGE: &'static [u8] = include_bytes!("../data/ProFont24Point.raw");
const CHAR_HEIGHT: u32 = 29;
const CHAR_HEIGHT: u32 = 30;
const CHAR_WIDTH: u32 = 16;
const FONT_IMAGE_WIDTH: u32 = 640;
const FONT_IMAGE_WIDTH: u32 = Self::CHAR_WIDTH * CHARS_PER_ROW;
fn char_offset(c: char) -> u32 {
let fallback = '?' as u32 - ' ' as u32;
if c < ' ' {
return fallback;
}
if c <= '~' {
return c as u32 - ' ' as u32;
}
if c < '¡' || c > 'ÿ' {
return fallback;
}
c as u32 - ' ' as u32 - 34
char_offset_impl(c)
}
}
/// The 24 point size.
/// The 24 point size with a character size of 16x30 pixels.
pub type ProFont24Point<'a, C> = FontBuilder<'a, C, ProFont24PointConf>;
#[cfg(test)]
mod tests {
use super::*;
use embedded_graphics::coord::Coord;
use embedded_graphics::dev::TestPixelColor;
use embedded_graphics::fonts::Font;
use mock_display::Display;
use embedded_graphics::style::Style;
use embedded_graphics::style::WithStyle;
use embedded_graphics::transform::Transform;
use embedded_graphics::unsignedcoord::UnsignedCoord;
use embedded_graphics::Drawing;
#[test]
fn off_screen_text_does_not_infinite_loop() {
let text: ProFont9Point<TestPixelColor> = ProFont9Point::render_str("Hello World!")
.with_style(Style::with_stroke(1u8.into()))
.translate(Coord::new(5, -10));
let mut it = text.into_iter();
assert_eq!(it.next(), None);
}
#[test]
fn unstroked_text_does_not_infinite_loop() {
let text: ProFont9Point<TestPixelColor> = ProFont9Point::render_str("Hello World!")
.with_style(Style::with_stroke(1u8.into()))
.translate(Coord::new(5, -10));
let mut it = text.into_iter();
assert_eq!(it.next(), None);
}
#[test]
fn text_dimensions() {
let hello: ProFont9Point<TestPixelColor> = ProFont9Point::render_str("Hello World!");
let empty: ProFont9Point<TestPixelColor> = ProFont9Point::render_str("");
assert_eq!(hello.dimensions(), UnsignedCoord::new(72, 8));
assert_eq!(empty.dimensions(), UnsignedCoord::new(0, 0));
}
#[test]
fn default_style() {
let mut display_default = Display::default();
display_default.draw(ProFont9Point::render_str("Mm").into_iter());
let mut display_full_style = Display::default();
display_full_style.draw(
ProFont9Point::render_str("Mm")
.with_stroke(Some(1u8.into()))
.with_fill(Some(0u8.into()))
.into_iter(),
);
let mut display_stroke = Display::default();
display_stroke.draw(
ProFont9Point::render_str("Mm")
.with_stroke(Some(1u8.into()))
.into_iter(),
);
let mut display_fill = Display::default();
display_fill.draw(
ProFont9Point::render_str("Mm")
.with_fill(Some(0u8.into()))
.into_iter(),
);
assert_eq!(display_default, display_full_style);
assert_eq!(display_default, display_stroke);
assert_eq!(display_default, display_fill);
}
#[test]
fn correct_m() {
let mut display = Display::default();
display.draw(
ProFont9Point::render_str("Mm")
.with_stroke(Some(1u8.into()))
.into_iter(),
);
assert_eq!(
display,
Display([
[1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
//
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
])
);
}
#[test]
fn correct_inverse_coloured_m() {
let mut display = Display::default();
display.draw(
ProFont9Point::render_str("Mm")
.with_stroke(Some(0u8.into()))
.with_fill(Some(1u8.into()))
.into_iter(),
);
assert_eq!(
display,
Display([
[0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
//
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
])
);
}
// tests if black on white has really the same behaviour as white on black
#[test]
fn compare_inverse_coloured_m() {
let mut display_inverse = Display::default();
display_inverse.draw(
ProFont9Point::render_str("Mm")
.with_stroke(Some(0u8.into()))
.with_fill(Some(1u8.into()))
.into_iter(),
);
let mut display_normal = Display::default();
display_normal.draw(
ProFont9Point::render_str("Mm")
.with_stroke(Some(1u8.into()))
.with_fill(Some(0u8.into()))
.into_iter(),
);
for (x, y) in display_inverse.0[0..8]
.iter()
.zip(display_normal.0[0..8].iter())
{
for (x2, y2) in x[0..12].iter().zip(y[0..12].iter()) {
assert_ne!(x2, y2);
}
}
}
#[test]
fn correct_ascii_borders() {
let mut display = Display::default();
display.draw(
ProFont9Point::render_str(" ~")
.with_stroke(Some(1u8.into()))
.into_iter(),
);
assert_eq!(
display,
Display([
[0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
//
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
])
);
}
#[test]
fn correct_dollar_y() {
let mut display = Display::default();
display.draw(
ProFont9Point::render_str("$y")
.with_stroke(Some(1u8.into()))
.into_iter(),
);
assert_eq!(
display,
Display([
[0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
//
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
])
);
}
#[test]
fn correct_latin1() {
let mut display = Display::default();
display.draw(
ProFont9Point::render_str("¡ÿ")
.with_stroke(Some(1u8.into()))
.into_iter(),
);
assert_eq!(
display,
Display([
[0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
//
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
])
);
}
#[test]
fn dont_panic() {
#[cfg_attr(rustfmt, rustfmt_skip)]
let two_question_marks = Display(
[
[0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
//
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
]
);
let mut display = Display::default();
display.draw(
ProFont9Point::render_str("\0\n")
.with_stroke(Some(1u8.into()))
.into_iter(),
);
assert_eq!(display, two_question_marks);
let mut display = Display::default();
display.draw(
ProFont9Point::render_str("\x7F\u{A0}")
.with_stroke(Some(1u8.into()))
.into_iter(),
);
assert_eq!(display, two_question_marks);
let mut display = Display::default();
display.draw(
ProFont9Point::render_str("Ā💣")
.with_stroke(Some(1u8.into()))
.into_iter(),
);
assert_eq!(display, two_question_marks);
}
}

149
src/main.rs

@ -0,0 +1,149 @@
#[cfg(feature = "exe")]
fn main () {
exe::main();
}
#[cfg(not(feature = "exe"))]
fn main() {
println!("profont must be compiled with the exe feature to enable the binary.");
}
#[cfg(feature = "exe")]
mod exe {
extern crate euclid;
extern crate font_kit;
extern crate image;
use self::euclid::{Point2D, Rect, 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 std::char;
use std::env;
use std::sync::Arc;
struct Glyph {
id: u32,
raster_rect: Rect<i32>,
}
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 font_size: f32 = env::args()
.nth(1)
.expect("font size argument must be supplied on command line")
.parse()
.expect("invalid font size");
let metrics = font.metrics();
let offset = (metrics.descent as f32 / metrics.units_per_em as f32 * font_size).round() as i32;
// Print latin 1 characters
let basic = (' ' as u32..='~' as u32)
.into_iter()
.map(|c| char::from_u32(c).unwrap());
let extended = ('\u{00A0}' as u32..='ÿ' as u32)
.into_iter()
.map(|c| char::from_u32(c).unwrap());
let all_chars: Vec<_> = basic.chain(extended).collect();
// Get the raster bounds of all chars
let glyphs: Vec<_> = all_chars
.iter()
.map(|&chr| {
font.glyph_for_char(chr).map(|glyph_id| {
let raster_rect = font
.raster_bounds(
glyph_id,
font_size,
&Point2D::zero(),
HintingOptions::None,
RasterizationOptions::Bilevel,
).expect("unable to get raster bounds");
Glyph {
id: glyph_id,
raster_rect,
}
})
}).collect();
// Work out how big the glyphs are
let char_size = Size2D::new(
glyphs
.iter()
.map(|glyph| {
glyph
.as_ref()
.map(|glyph| glyph.raster_rect.size.width)
.unwrap_or(0)
}).max()
.unwrap(),
glyphs
.iter()
.map(|glyph| {
glyph
.as_ref()
.map(|glyph| glyph.raster_rect.size.height)
.unwrap_or(0)
}).max()
.unwrap(),
).to_u32();
// 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,
);
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);
font.rasterize_glyph(
&mut canvas,
glyph.id,
font_size,
&glyph.raster_rect.origin.to_f32(),
HintingOptions::None,
RasterizationOptions::Bilevel,
).expect("error rasterizing glyph");
let col = i as u32 % row_size;
let row = i as u32 / row_size;
let img_x = col * char_size.width;
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()
{
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 {
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;
if pixel_x >= 0 && pixel_y >= 0 {
imgbuf.put_pixel(pixel_x as u32, pixel_y as u32, Luma([0xFFu8]));
}
}
}
}
}
}
let filename = format!("ProFont{}Point.png", font_size);
imgbuf.save(&filename).expect("error saving PNG");
println!("Wrote {} with character size of {}", filename, char_size);
}
}
Loading…
Cancel
Save