pub use footile;
mod tt;
pub use footile::PathOp;
use unicode_normalization::UnicodeNormalization;
use std::fmt;
use std::sync::Arc;
#[derive(Copy, Clone)]
struct Vec2(pub f32, pub f32);
pub struct FontGroup<'a> {
fonts: Vec<Font<'a>>,
mono: Option<f32>,
}
#[cfg(feature = "builtin-font")]
impl<'a> Default for FontGroup<'a> {
fn default() -> Self {
const FONTA: &[u8] = include_bytes!("font/dejavu/DejaVuSansMono.ttf");
const FONTB: &[u8] = include_bytes!("font/wqy-microhei/WenQuanYiMicroHeiMono.ttf");
FontGroup::new()
.add(FONTA)
.unwrap()
.add(FONTB)
.unwrap()
.multilingual_mono(1)
}
}
impl<'a> FontGroup<'a> {
pub fn new() -> Self {
FontGroup {
fonts: vec![],
mono: None,
}
}
pub fn add<B: Into<SharedBytes<'a>>>(mut self, bytes: B) -> Result<Self, Error> {
let collection = FontCollection::new(bytes)?;
if tt::get_font_offset_for_index(&collection.0, 1).is_some() {
let mut fonts = collection.into_fonts();
self.fonts.append(&mut fonts);
} else {
let font = collection.into_font()?;
self.fonts.push(font);
}
Ok(self)
}
fn glyphs<T: ToString>(
&'a self,
text: T,
scale: (f32, f32),
mono: Option<f32>,
) -> GlyphIterator<'a> {
let (scale_x, scale_y) = {
let scale_y = self.fonts[0].info.scale_for_pixel_height(scale.1);
let scale_x = scale_y * scale.0 / scale.1;
(scale_x, scale_y)
};
GlyphIterator {
font: &self.fonts,
api_scale: scale,
scale: Vec2(scale_x, scale_y),
string: text.to_string().nfc().collect::<Vec<char>>(),
cursor: 0,
last: None,
mono,
}
}
pub fn render<T: ToString>(&self, text: T, xy: (f32, f32), wh: (f32, f32)) -> PathIterator {
PathIterator::new(self.glyphs(text, wh, self.mono), xy)
}
pub fn multilingual_mono(mut self, index: usize) -> Self {
let glyphb = self.fonts[index].glyph('野', Vec2(1.0, 1.0), None).0;
let glyphb = self.fonts[index]
.info
.get_glyph_h_metrics(glyphb.id().0)
.advance_width as f32;
self.mono = Some(glyphb);
self
}
}
#[derive(Clone, Debug)]
struct FontCollection<'a>(SharedBytes<'a>);
#[derive(Clone)]
struct Font<'a> {
info: tt::FontInfo<SharedBytes<'a>>,
}
#[derive(Clone, Debug)]
pub enum SharedBytes<'a> {
ByRef(&'a [u8]),
ByArc(Arc<[u8]>),
}
impl<'a> ::std::ops::Deref for SharedBytes<'a> {
type Target = [u8];
fn deref(&self) -> &[u8] {
match *self {
SharedBytes::ByRef(bytes) => bytes,
SharedBytes::ByArc(ref bytes) => &**bytes,
}
}
}
impl<'a> From<&'a [u8]> for SharedBytes<'a> {
fn from(bytes: &'a [u8]) -> SharedBytes<'a> {
SharedBytes::ByRef(bytes)
}
}
impl From<Arc<[u8]>> for SharedBytes<'static> {
fn from(bytes: Arc<[u8]>) -> SharedBytes<'static> {
SharedBytes::ByArc(bytes)
}
}
impl From<Box<[u8]>> for SharedBytes<'static> {
fn from(bytes: Box<[u8]>) -> SharedBytes<'static> {
SharedBytes::ByArc(bytes.into())
}
}
impl From<Vec<u8>> for SharedBytes<'static> {
fn from(bytes: Vec<u8>) -> SharedBytes<'static> {
SharedBytes::ByArc(bytes.into())
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
struct Codepoint(pub u32);
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
struct GlyphId(pub u32);
#[derive(Clone)]
struct Glyph<'a> {
inner: GlyphInner<'a>,
v: Vec2,
}
#[derive(Clone)]
struct GlyphInner<'a>(Font<'a>, u32);
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
struct HMetrics {
pub advance_width: f32,
pub left_side_bearing: f32,
}
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
struct VMetrics {
pub ascent: f32,
pub descent: f32,
pub line_gap: f32,
}
impl From<tt::VMetrics> for VMetrics {
fn from(vm: tt::VMetrics) -> Self {
Self {
ascent: vm.ascent as f32,
descent: vm.descent as f32,
line_gap: vm.line_gap as f32,
}
}
}
trait IntoGlyphId {
fn into_glyph_id(self, a: &Font) -> GlyphId;
}
impl IntoGlyphId for char {
fn into_glyph_id(self, font: &Font) -> GlyphId {
GlyphId(font.info.find_glyph_index(self as u32))
}
}
impl IntoGlyphId for Codepoint {
fn into_glyph_id(self, font: &Font) -> GlyphId {
GlyphId(font.info.find_glyph_index(self.0))
}
}
impl IntoGlyphId for GlyphId {
fn into_glyph_id(self, _font: &Font) -> GlyphId {
self
}
}
impl<'a> FontCollection<'a> {
pub fn new<B: Into<SharedBytes<'a>>>(bytes: B) -> Result<FontCollection<'a>, Error> {
let bytes = bytes.into();
if !tt::is_font(&bytes) && &bytes[0..4] != b"ttcf" {
return Err(Error::UnrecognizedFormat);
}
Ok(FontCollection(bytes))
}
pub fn into_font(self) -> Result<Font<'a>, Error> {
let offset = if tt::is_font(&self.0) {
0
} else if tt::get_font_offset_for_index(&self.0, 1).is_some() {
return Err(Error::CollectionContainsMultipleFonts);
} else {
match tt::get_font_offset_for_index(&self.0, 0) {
None => return Err(Error::IllFormed),
Some(offset) => offset,
}
};
let info = tt::FontInfo::new(self.0, offset as usize).ok_or(Error::IllFormed)?;
Ok(Font { info })
}
pub fn font_at(&self, i: usize) -> Result<Font<'a>, Error> {
let offset = tt::get_font_offset_for_index(&self.0, i as i32)
.ok_or(Error::CollectionIndexOutOfBounds)?;
let info = tt::FontInfo::new(self.0.clone(), offset as usize).ok_or(Error::IllFormed)?;
Ok(Font { info })
}
pub fn into_fonts(self) -> Vec<Font<'a>> {
let mut fonts = vec![];
let mut index = 0;
loop {
let result = self.font_at(index);
if let Err(Error::CollectionIndexOutOfBounds) = result {
break;
}
index += 1;
fonts.push(result.unwrap());
}
fonts
}
}
pub struct PathIterator<'a> {
glyph_iter: GlyphIterator<'a>,
glyph: Option<Glyph<'a>>,
x: f32,
y: f32,
cx: f32,
cy: f32,
oc: usize,
vt: bool,
rl: bool,
ch: char,
advance: f32,
f: f32,
}
static mut OP: PathOp = PathOp::Close();
impl<'a> PathIterator<'a> {
fn new(glyph_iter: GlyphIterator<'a>, xy: (f32, f32)) -> Self {
PathIterator {
glyph_iter,
glyph: None,
x: xy.0,
y: xy.1,
cx: xy.0,
cy: xy.1,
oc: 0,
vt: false,
rl: false,
ch: '\0',
advance: 0.0,
f: 0.0,
}
}
pub fn vertical(mut self) -> Self {
self.vt = true;
self
}
pub fn right_to_left(mut self) -> Self {
self.rl = true;
self
}
pub fn xy(self) -> (f32, f32) {
(self.cx, self.cy)
}
}
impl<'a> Iterator for &mut PathIterator<'a> {
type Item = &'static PathOp;
fn next(&mut self) -> Option<Self::Item> {
if self.ch == '\0' {
self.ch = '\x01';
return Some(&PathOp::PenWidth(0.0));
}
if self.glyph.is_none() {
let (glyph, advance) =
if let Some(ch) = self.glyph_iter.string.get(self.glyph_iter.cursor) {
self.ch = *ch;
self.glyph_iter.next().unwrap()
} else {
if self.vt {
self.f += self.advance;
self.cy += 0.0;
} else {
self.cx += if self.rl { -self.advance } else { self.advance };
}
return None;
};
if self.ch == '\n' {
self.advance = 0.0;
if self.vt {
self.cy = self.y;
self.cx += if self.rl { -1.0 } else { 1.0 } * glyph.font().v_metrics(glyph.v);
self.f = 0.0;
} else {
self.cx = self.x;
self.cy += glyph.font().v_metrics(glyph.v);
}
return Self::next(self);
} else if self.ch == ' ' && self.vt {
self.advance = 0.0;
self.f = 0.0;
self.cy += glyph.font().v_metrics(glyph.v) * 2.0;
return Self::next(self);
} else if self.ch == '\t' {
if self.vt {
self.advance = 0.0;
self.f = 0.0;
self.cy += glyph.font().v_metrics(glyph.v);
} else {
self.cx += glyph.font().v_metrics(glyph.v) * if self.rl { -4.0 } else { 4.0 };
}
return Self::next(self);
}
self.oc = 0;
if self.vt {
self.f += self.advance;
if self.ch == '.' {
self.f = glyph.font().v_metrics(glyph.v) - advance;
} else if self.f + advance >= glyph.font().v_metrics(glyph.v)
&& advance <= glyph.font().v_metrics(glyph.v)
{
self.f = 0.0;
self.cy += glyph.font().v_metrics(glyph.v);
}
} else {
self.cx += if self.rl { -self.advance } else { self.advance };
}
self.advance = if let Some(a) = self.glyph_iter.mono {
if unicode_width::UnicodeWidthChar::width(self.ch) == Some(2) {
self.glyph_iter.api_scale.0
} else {
self.glyph_iter.api_scale.0 * 0.5
}
} else {
advance
};
self.glyph = Some(glyph);
}
let shape = {
let glyph = self.glyph.as_ref().unwrap();
let (font, id) = (glyph.font(), glyph.id());
font.info.get_glyph_shape(id.0).unwrap_or_else(Vec::new)
};
let v = if let Some(v) = shape.get(self.oc) {
v
} else {
self.glyph = None;
return Self::next(self);
};
let glyph = self.glyph.as_ref().unwrap();
let ay = glyph.font().v_metrics(glyph.v);
let x = v.x as f32 * glyph.v.0
+ self.cx
+ if self.rl {
-self.advance - self.f
} else {
self.f
};
let y = -v.y as f32 * glyph.v.1 + self.cy + ay;
use crate::tt::VertexType;
match v.vertex_type() {
VertexType::LineTo => unsafe { OP = PathOp::Line(x, y) },
VertexType::CurveTo => {
let cx = v.cx as f32 * glyph.v.0
+ self.cx
+ if self.rl {
-self.advance - self.f
} else {
self.f
};
let cy = -v.cy as f32 * glyph.v.1 + self.cy + ay;
unsafe { OP = PathOp::Quad(cx, cy, x, y) };
}
VertexType::CubicTo => {
let cx = v.cx as f32 * glyph.v.0
+ self.cx
+ if self.rl {
-self.advance - self.f
} else {
self.f
};
let cy = -v.cy as f32 * glyph.v.1 + self.cy + ay;
let bx = v.bx as f32 * glyph.v.0
+ self.cx
+ if self.rl {
-self.advance - self.f
} else {
self.f
};
let by = -v.by as f32 * glyph.v.1 + self.cy + ay;
unsafe { OP = PathOp::Cubic(cx, cy, bx, by, x, y) };
},
VertexType::MoveTo => unsafe { OP = PathOp::Move(x, y) },
}
self.oc += 1;
unsafe { Some(&OP) }
}
}
struct GlyphIterator<'a> {
font: &'a Vec<Font<'a>>,
api_scale: (f32, f32),
scale: Vec2,
string: Vec<char>,
cursor: usize,
last: Option<(Glyph<'a>, &'a Font<'a>)>,
mono: Option<f32>,
}
impl<'a> Iterator for GlyphIterator<'a> {
type Item = (Glyph<'a>, f32);
fn next(&mut self) -> Option<(Glyph<'a>, f32)> {
let c = self.string.get(self.cursor);
if let Some(c) = c {
let mut i = 0;
let glyph: Glyph<'a> = loop {
let mono = if self.mono.is_some()
&& unicode_width::UnicodeWidthChar::width(*c) == Some(1)
{
self.mono
} else {
None
};
let (glyph, hit): (Glyph<'a>, bool) = self.font[i].glyph(*c, self.scale, mono);
if hit || i == self.font.len() - 1 {
break glyph;
}
i += 1;
};
let mut advance = self.font[i]
.info
.get_glyph_h_metrics(glyph.id().0)
.advance_width as f32
* self.scale.0;
if self.cursor != 0 {
advance += self.font[i].kerning(
self.api_scale,
self.scale,
self.last.as_ref().unwrap(),
&glyph,
);
}
self.last = Some((glyph.clone(), &self.font[i]));
self.cursor += 1;
Some((glyph, advance))
} else {
None
}
}
}
impl<'a> Font<'a> {
fn v_metrics(&self, scale: Vec2) -> f32 {
let vm = self.info.get_v_metrics();
let scale = scale.1;
(vm.ascent as f32) * scale
}
fn glyph_count(&self) -> usize {
self.info.get_num_glyphs() as usize
}
fn glyph<C: IntoGlyphId>(&self, id: C, mut v: Vec2, mono: Option<f32>) -> (Glyph<'a>, bool) {
let gid = id.into_glyph_id(self);
if let Some(glyphb) = mono {
let glypha = self.glyph('a', v, None).0;
let glypha = self.info.get_glyph_h_metrics(glypha.id().0).advance_width as f32;
v.0 *= 0.5 * glyphb / glypha;
}
assert!((gid.0 as usize) < self.glyph_count());
(Glyph::new(GlyphInner(self.clone(), gid.0), v), gid.0 != 0)
}
fn pair_kerning<A, B>(
&self,
scale: (f32, f32),
v: Vec2,
first: A,
second: B,
old: &'a Font<'a>,
) -> f32
where
A: IntoGlyphId,
B: IntoGlyphId,
{
let (first, second) = (old.glyph(first, v, None).0, self.glyph(second, v, None).0);
let factor = self.info.scale_for_pixel_height(scale.1) * (scale.0 / scale.1);
let kern = self
.info
.get_glyph_kern_advance(first.id().0, second.id().0);
factor * kern as f32
}
fn kerning(
&self,
scale: (f32, f32),
v: Vec2,
first: &(Glyph<'a>, &'a Font<'a>),
second: &Glyph<'a>,
) -> f32 {
self.pair_kerning(scale, v, first.0.id(), second.id(), first.1)
}
}
impl<'a> Glyph<'a> {
fn new(inner: GlyphInner<'a>, v: Vec2) -> Glyph<'a> {
Glyph { inner, v }
}
fn font(&self) -> &Font<'a> {
&self.inner.0
}
fn id(&self) -> GlyphId {
GlyphId(self.inner.1)
}
}
#[derive(Debug)]
pub enum Error {
UnrecognizedFormat,
IllFormed,
CollectionIndexOutOfBounds,
CollectionContainsMultipleFonts,
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> std::result::Result<(), fmt::Error> {
f.write_str(std::error::Error::description(self))
}
}
impl std::error::Error for Error {
fn description(&self) -> &str {
use self::Error::*;
match *self {
UnrecognizedFormat => "Font data in unrecognized format",
IllFormed => "Font data is ill-formed",
CollectionIndexOutOfBounds => "Font collection has no font at the given index",
CollectionContainsMultipleFonts => {
"Attempted to convert collection into a font, \
but collection contais more than one font"
}
}
}
}
impl std::convert::From<Error> for std::io::Error {
fn from(error: Error) -> Self {
std::io::Error::new(std::io::ErrorKind::Other, error)
}
}