#![warn(clippy::all)]
#![allow(
clippy::too_many_arguments,
clippy::cast_lossless,
clippy::many_single_char_names
)]
#![cfg_attr(not(feature = "std"), no_std)]
extern crate alloc;
use alloc::vec::Vec;
use byteorder::{BigEndian as BE, ByteOrder};
use core::ops::Deref;
#[cfg(all(feature = "libm", not(feature = "std")))]
use libm::F32Ext;
#[cfg(not(any(feature = "libm", feature = "std")))]
compile_error!("You need to activate either the `std` or `libm` feature.");
#[derive(Copy, Clone, Debug)]
pub struct FontInfo<Data: Deref<Target = [u8]>> {
data: Data,
num_glyphs: u32,
loca: u32,
head: u32,
glyf: u32,
hhea: u32,
hmtx: u32,
name: u32,
kern: u32,
index_map: u32,
index_to_loc_format: u32,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[repr(C)]
pub struct Vertex {
pub x: i16,
pub y: i16,
pub cx: i16,
pub cy: i16,
type_: u8,
}
impl Vertex {
pub fn vertex_type(&self) -> VertexType {
match self.type_ {
1 => VertexType::MoveTo,
2 => VertexType::LineTo,
3 => VertexType::CurveTo,
type_ => panic!("Invalid vertex type: {}", type_),
}
}
}
#[test]
fn test_vertex_type() {
fn v(type_: VertexType) -> Vertex {
Vertex {
x: 0,
y: 0,
cx: 0,
cy: 0,
type_: type_ as u8,
}
}
assert_eq!(v(VertexType::MoveTo).vertex_type(), VertexType::MoveTo);
assert_eq!(v(VertexType::LineTo).vertex_type(), VertexType::LineTo);
assert_eq!(v(VertexType::CurveTo).vertex_type(), VertexType::CurveTo);
}
#[test]
#[should_panic]
fn test_invalid_vertex_type() {
let v = Vertex {
x: 0,
y: 0,
cx: 0,
cy: 0,
type_: 255,
};
let s = match v.vertex_type() {
VertexType::MoveTo => "move to",
VertexType::LineTo => "line to",
VertexType::CurveTo => "curve to",
};
println!("{}", s);
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[repr(u8)]
pub enum VertexType {
MoveTo = 1,
LineTo = 2,
CurveTo = 3,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct Rect<T> {
pub x0: T,
pub y0: T,
pub x1: T,
pub y1: T,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct HMetrics {
pub advance_width: i32,
pub left_side_bearing: i32,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct VMetrics {
pub ascent: i32,
pub descent: i32,
pub line_gap: i32,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(C)]
pub enum PlatformId {
Unicode = 0,
Mac = 1,
Iso = 2,
Microsoft = 3,
}
fn platform_id(v: u16) -> Option<PlatformId> {
use crate::PlatformId::*;
match v {
0 => Some(Unicode),
1 => Some(Mac),
2 => Some(Iso),
3 => Some(Microsoft),
_ => None,
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(C)]
#[allow(non_camel_case_types)]
pub enum UnicodeEid {
Unicode_1_0 = 0,
Unicode_1_1 = 1,
Iso_10646 = 2,
Unicode_2_0_Bmp = 3,
Unicode_2_0_Full = 4,
}
fn unicode_eid(v: u16) -> Option<UnicodeEid> {
use crate::UnicodeEid::*;
match v {
0 => Some(Unicode_1_0),
1 => Some(Unicode_1_1),
2 => Some(Iso_10646),
3 => Some(Unicode_2_0_Bmp),
4 => Some(Unicode_2_0_Full),
_ => None,
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(C)]
pub enum MicrosoftEid {
Symbol = 0,
UnicodeBMP = 1,
Shiftjis = 2,
UnicodeFull = 10,
}
fn microsoft_eid(v: u16) -> Option<MicrosoftEid> {
use crate::MicrosoftEid::*;
match v {
0 => Some(Symbol),
1 => Some(UnicodeBMP),
2 => Some(Shiftjis),
10 => Some(UnicodeFull),
_ => None,
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(C)]
pub enum MacEid {
Roman = 0,
Arabic = 4,
Japanese = 1,
Hebrew = 5,
ChineseTrad = 2,
Greek = 6,
Korean = 3,
Russian = 7,
}
fn mac_eid(v: u16) -> Option<MacEid> {
use crate::MacEid::*;
match v {
0 => Some(Roman),
1 => Some(Japanese),
2 => Some(ChineseTrad),
3 => Some(Korean),
4 => Some(Arabic),
5 => Some(Hebrew),
6 => Some(Greek),
7 => Some(Russian),
_ => None,
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(C)]
pub enum MicrosoftLang {
English = 0x0409,
Italian = 0x0410,
Chinese = 0x0804,
Japanese = 0x0411,
Dutch = 0x0413,
Korean = 0x0412,
French = 0x040c,
Russian = 0x0419,
German = 0x0407,
Hebrew = 0x040d,
Swedish = 0x041D,
}
fn microsoft_lang(v: u16) -> Option<MicrosoftLang> {
use crate::MicrosoftLang::*;
match v {
0x0409 => Some(English),
0x0804 => Some(Chinese),
0x0413 => Some(Dutch),
0x040c => Some(French),
0x0407 => Some(German),
0x040d => Some(Hebrew),
0x0410 => Some(Italian),
0x0411 => Some(Japanese),
0x0412 => Some(Korean),
0x0419 => Some(Russian),
0x041D => Some(Swedish),
_ => None,
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(C)]
pub enum MacLang {
English = 0,
Japanese = 11,
Arabic = 12,
Korean = 23,
Dutch = 4,
Russian = 32,
French = 1,
Spanish = 6,
German = 2,
Swedish = 5,
Hebrew = 10,
ChineseSimplified = 33,
Italian = 3,
ChineseTrad = 19,
}
fn mac_lang(v: u16) -> Option<MacLang> {
use crate::MacLang::*;
match v {
0 => Some(English),
12 => Some(Arabic),
4 => Some(Dutch),
1 => Some(French),
2 => Some(German),
10 => Some(Hebrew),
3 => Some(Italian),
11 => Some(Japanese),
23 => Some(Korean),
32 => Some(Russian),
6 => Some(Spanish),
5 => Some(Swedish),
33 => Some(ChineseSimplified),
19 => Some(ChineseTrad),
_ => None,
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum PlatformEncodingLanguageId {
Unicode(Option<Result<UnicodeEid, u16>>, Option<u16>),
Mac(Option<Result<MacEid, u16>>, Option<Result<MacLang, u16>>),
Iso(Option<u16>, Option<u16>),
Microsoft(
Option<Result<MicrosoftEid, u16>>,
Option<Result<MicrosoftLang, u16>>,
),
}
fn platform_encoding_id(
platform_id: PlatformId,
encoding_id: Option<u16>,
language_id: Option<u16>,
) -> PlatformEncodingLanguageId {
match platform_id {
PlatformId::Unicode => PlatformEncodingLanguageId::Unicode(
encoding_id.map(|id| unicode_eid(id).ok_or(id)),
language_id,
),
PlatformId::Mac => PlatformEncodingLanguageId::Mac(
encoding_id.map(|id| mac_eid(id).ok_or(id)),
language_id.map(|id| mac_lang(id).ok_or(id)),
),
PlatformId::Iso => PlatformEncodingLanguageId::Iso(encoding_id, language_id),
PlatformId::Microsoft => PlatformEncodingLanguageId::Microsoft(
encoding_id.map(|id| microsoft_eid(id).ok_or(id)),
language_id.map(|id| microsoft_lang(id).ok_or(id)),
),
}
}
pub fn is_font(data: &[u8]) -> bool {
if data.len() >= 4 {
let tag = &data[0..4];
tag == [b'1', 0, 0, 0] || tag == b"typ1" || tag == b"OTTO" || tag == [0, 1, 0, 0]
} else {
false
}
}
pub fn is_collection(data: &[u8]) -> bool {
data.len() >= 4 && &data[0..4] == b"ttcf"
}
fn find_table(data: &[u8], fontstart: usize, tag: &[u8]) -> u32 {
let num_tables = BE::read_u16(&data[fontstart + 4..]);
let tabledir = fontstart + 12;
for i in 0..num_tables {
let loc = tabledir + 16 * (i as usize);
if &data[loc..loc + 4] == tag {
return BE::read_u32(&data[loc + 8..]);
}
}
0
}
pub fn get_font_offset_for_index(font_collection: &[u8], index: i32) -> Option<u32> {
if is_font(font_collection) {
return if index == 0 { Some(0) } else { None };
}
if is_collection(font_collection)
&& (BE::read_u32(&font_collection[4..]) == 0x0001_0000
|| BE::read_u32(&font_collection[4..]) == 0x0002_0000)
{
let n = BE::read_i32(&font_collection[8..]);
if index >= n {
return None;
}
return Some(BE::read_u32(&font_collection[12 + (index as usize) * 4..]));
}
None
}
macro_rules! read_ints {
($n:expr, i16, $data:expr) => {{
let mut nums = [0; $n];
let data = $data;
BE::read_i16_into(&data[..$n * 2], &mut nums);
nums
}};
($n:expr, u16, $data:expr) => {{
let mut nums = [0; $n];
let data = $data;
BE::read_u16_into(&data[..$n * 2], &mut nums);
nums
}};
($n:expr, u32, $data:expr) => {{
let mut nums = [0; $n];
let data = $data;
BE::read_u32_into(&data[..$n * 4], &mut nums);
nums
}};
}
impl<Data: Deref<Target = [u8]>> FontInfo<Data> {
pub fn new(data: Data, fontstart: usize) -> Option<FontInfo<Data>> {
let cmap = find_table(&data, fontstart, b"cmap");
let loca = find_table(&data, fontstart, b"loca");
let head = find_table(&data, fontstart, b"head");
let glyf = find_table(&data, fontstart, b"glyf");
let hhea = find_table(&data, fontstart, b"hhea");
let hmtx = find_table(&data, fontstart, b"hmtx");
let name = find_table(&data, fontstart, b"name");
let kern = find_table(&data, fontstart, b"kern");
if cmap == 0 || loca == 0 || head == 0 || glyf == 0 || hhea == 0 || hmtx == 0 {
return None;
}
let t = find_table(&data, fontstart, b"maxp");
let num_glyphs = if t != 0 {
BE::read_u16(&data[t as usize + 4..])
} else {
0xffff
};
let num_tables = BE::read_u16(&data[cmap as usize + 2..]);
let mut index_map = 0;
for i in 0..num_tables {
let encoding_record = (cmap + 4 + 8 * (i as u32)) as usize;
match platform_id(BE::read_u16(&data[encoding_record..])) {
Some(PlatformId::Microsoft) => {
match microsoft_eid(BE::read_u16(&data[encoding_record + 2..])) {
Some(MicrosoftEid::UnicodeBMP) | Some(MicrosoftEid::UnicodeFull) => {
index_map = cmap + BE::read_u32(&data[encoding_record + 4..]);
}
_ => (),
}
}
Some(PlatformId::Unicode) => {
index_map = cmap + BE::read_u32(&data[encoding_record + 4..]);
}
_ => (),
}
}
if index_map == 0 {
return None;
}
let index_to_loc_format = BE::read_u16(&data[head as usize + 50..]) as u32;
Some(FontInfo {
data,
loca,
head,
glyf,
hhea,
hmtx,
name,
kern,
num_glyphs: num_glyphs as u32,
index_map,
index_to_loc_format,
})
}
pub fn get_num_glyphs(&self) -> u32 {
self.num_glyphs
}
pub fn find_glyph_index(&self, unicode_codepoint: u32) -> u32 {
let data = &self.data;
let index_map = &data[self.index_map as usize..];
let format = BE::read_u16(index_map);
match format {
0 => {
let bytes = BE::read_u16(&index_map[2..]);
if unicode_codepoint < bytes as u32 - 6 {
return index_map[6 + unicode_codepoint as usize] as u32;
}
0
}
6 => {
let first = BE::read_u16(&index_map[6..]) as u32;
let count = BE::read_u16(&index_map[8..]) as u32;
if unicode_codepoint >= first && unicode_codepoint < first + count {
return BE::read_u16(&index_map[10 + (unicode_codepoint - first) as usize * 2..])
as u32;
}
0
}
2 => {
panic!("Index map format unsupported: 2");
}
4 => {
let segcount = BE::read_u16(&index_map[6..]) as usize >> 1;
let mut search_range = BE::read_u16(&index_map[8..]) as usize >> 1;
let mut entry_selector = BE::read_u16(&index_map[10..]);
let range_shift = BE::read_u16(&index_map[12..]) as usize >> 1;
let end_count = self.index_map as usize + 14;
let mut search = end_count;
if unicode_codepoint > 0xffff {
return 0;
}
if unicode_codepoint >= BE::read_u16(&data[search + range_shift * 2..]) as u32 {
search += range_shift * 2;
}
search -= 2;
while entry_selector != 0 {
search_range >>= 1;
let end = BE::read_u16(&data[search + search_range * 2..]) as u32;
if unicode_codepoint > end {
search += search_range * 2;
}
entry_selector -= 1;
}
search += 2;
{
let item = (search - end_count) >> 1;
assert!(
unicode_codepoint <= BE::read_u16(&data[end_count + 2 * item..]) as u32
);
let start = BE::read_u16(&index_map[14 + segcount * 2 + 2 + 2 * item..]) as u32;
if unicode_codepoint < start {
return 0;
}
let offset =
BE::read_u16(&index_map[14 + segcount * 6 + 2 + 2 * item..]) as usize;
if offset == 0 {
return (unicode_codepoint as i32
+ BE::read_i16(&index_map[14 + segcount * 4 + 2 + 2 * item..]) as i32)
as u16 as u32;
}
BE::read_u16(
&index_map[offset
+ (unicode_codepoint - start) as usize * 2
+ 14
+ segcount * 6
+ 2
+ 2 * item..],
) as u32
}
}
12 | 13 => {
let mut low = 0u32;
let mut high = BE::read_u32(&index_map[12..]);
let groups = &index_map[16..];
while low < high {
let mid = (low + high) / 2;
let mid12 = (mid * 12) as usize;
let group = &groups[mid12..mid12 + 12];
let start_char = BE::read_u32(group);
if unicode_codepoint < start_char {
high = mid;
} else if unicode_codepoint > BE::read_u32(&group[4..]) {
low = mid + 1;
} else {
let start_glyph = BE::read_u32(&group[8..]);
if format == 12 {
return start_glyph + unicode_codepoint - start_char;
} else {
return start_glyph;
}
}
}
0
}
n => panic!("Index map format unsupported: {}", n),
}
}
pub fn get_codepoint_shape(&self, unicode_codepoint: u32) -> Option<Vec<Vertex>> {
self.get_glyph_shape(self.find_glyph_index(unicode_codepoint))
}
fn get_glyf_offset(&self, glyph_index: u32) -> Option<u32> {
if glyph_index >= self.num_glyphs || self.index_to_loc_format >= 2 {
return None;
}
let [g1, g2] = if self.index_to_loc_format == 0 {
let d = &self.data[(self.loca + glyph_index * 2) as usize..];
let [g1, g2] = read_ints!(2, u16, d);
[g1 as u32 * 2, g2 as u32 * 2]
} else {
read_ints!(2, u32, &self.data[(self.loca + glyph_index * 4) as usize..])
};
if g1 == g2 {
None
} else {
Some(self.glyf + g1)
}
}
pub fn get_glyph_box(&self, glyph_index: u32) -> Option<Rect<i16>> {
let g = self.get_glyf_offset(glyph_index)? as usize;
let [x0, y0, x1, y1] = read_ints!(4, i16, &self.data[g + 2..]);
Some(Rect { x0, y0, x1, y1 })
}
pub fn get_codepoint_box(&self, codepoint: u32) -> Option<Rect<i16>> {
self.get_glyph_box(self.find_glyph_index(codepoint))
}
pub fn is_glyph_empty(&self, glyph_index: u32) -> bool {
match self.get_glyf_offset(glyph_index) {
Some(g) => {
let number_of_contours = BE::read_i16(&self.data[g as usize..]);
number_of_contours == 0
}
None => true,
}
}
pub fn get_glyph_shape(&self, glyph_index: u32) -> Option<Vec<Vertex>> {
let g = match self.get_glyf_offset(glyph_index) {
Some(g) => &self.data[g as usize..],
None => return None,
};
let number_of_contours = BE::read_i16(g);
let vertices: Vec<Vertex> = if number_of_contours > 0 {
self.glyph_shape_positive_contours(g, number_of_contours as usize)
} else if number_of_contours == -1 {
let mut more = true;
let mut comp = &g[10..];
let mut vertices = Vec::new();
while more {
let mut mtx = [1.0, 0.0, 0.0, 1.0, 0.0, 0.0];
let [flags, gidx] = read_ints!(2, i16, comp);
comp = &comp[4..];
let gidx = gidx as u16;
if flags & 2 != 0 {
if flags & 1 != 0 {
let [a, b] = read_ints!(2, i16, comp);
comp = &comp[4..];
mtx[4] = a as f32;
mtx[5] = b as f32;
} else {
mtx[4] = (comp[0] as i8) as f32;
mtx[5] = (comp[1] as i8) as f32;
comp = &comp[2..];
}
} else {
panic!("Matching points not supported.");
}
if flags & (1 << 3) != 0 {
mtx[0] = BE::read_i16(comp) as f32 / 16384.0;
comp = &comp[2..];
mtx[1] = 0.0;
mtx[2] = 0.0;
mtx[3] = mtx[0];
} else if flags & (1 << 6) != 0 {
let [a, b] = read_ints!(2, i16, comp);
comp = &comp[4..];
mtx[0] = a as f32 / 16384.0;
mtx[1] = 0.0;
mtx[2] = 0.0;
mtx[3] = b as f32 / 16384.0;
} else if flags & (1 << 7) != 0 {
let [a, b, c, d] = read_ints!(4, i16, comp);
comp = &comp[8..];
mtx[0] = a as f32 / 16384.0;
mtx[1] = b as f32 / 16384.0;
mtx[2] = c as f32 / 16384.0;
mtx[3] = d as f32 / 16384.0;
}
let m = (mtx[0] * mtx[0] + mtx[1] * mtx[1]).sqrt();
let n = (mtx[2] * mtx[2] + mtx[3] * mtx[3]).sqrt();
let mut comp_verts = self.get_glyph_shape(gidx as u32).unwrap_or_else(Vec::new);
if !comp_verts.is_empty() {
for v in &mut *comp_verts {
let (x, y, cx, cy) = (v.x as f32, v.y as f32, v.cx as f32, v.cy as f32);
*v = Vertex {
type_: v.type_,
x: (m * (mtx[0] * x + mtx[2] * y + mtx[4])) as i16,
y: (n * (mtx[1] * x + mtx[3] * y + mtx[5])) as i16,
cx: (m * (mtx[0] * cx + mtx[2] * cy + mtx[4])) as i16,
cy: (n * (mtx[1] * cx + mtx[3] * cy + mtx[5])) as i16,
};
}
vertices.append(&mut comp_verts);
}
more = flags & (1 << 5) != 0;
}
vertices
} else if number_of_contours < 0 {
panic!("Contour format not supported.")
} else {
return None;
};
Some(vertices)
}
#[inline]
fn glyph_shape_positive_contours(
&self,
glyph_data: &[u8],
number_of_contours: usize,
) -> Vec<Vertex> {
use crate::VertexType::*;
struct FlagData {
flags: u8,
x: i16,
y: i16,
}
#[inline]
fn close_shape(
vertices: &mut Vec<Vertex>,
was_off: bool,
start_off: bool,
sx: i16,
sy: i16,
scx: i16,
scy: i16,
cx: i16,
cy: i16,
) {
if start_off {
if was_off {
vertices.push(Vertex {
type_: CurveTo as u8,
x: (cx + scx) >> 1,
y: (cy + scy) >> 1,
cx,
cy,
});
}
vertices.push(Vertex {
type_: CurveTo as u8,
x: sx,
y: sy,
cx: scx,
cy: scy,
});
} else {
vertices.push(if was_off {
Vertex {
type_: CurveTo as u8,
x: sx,
y: sy,
cx,
cy,
}
} else {
Vertex {
type_: LineTo as u8,
x: sx,
y: sy,
cx: 0,
cy: 0,
}
});
}
}
let number_of_contours = number_of_contours as usize;
let mut start_off = false;
let mut was_off = false;
let end_points_of_contours = &glyph_data[10..];
let ins = BE::read_u16(&glyph_data[10 + number_of_contours * 2..]) as usize;
let mut points = &glyph_data[10 + number_of_contours * 2 + 2 + ins..];
let n = 1 + BE::read_u16(&end_points_of_contours[number_of_contours * 2 - 2..]) as usize;
let m = n + 2 * number_of_contours;
let mut vertices: Vec<Vertex> = Vec::with_capacity(m);
let mut flag_data = Vec::with_capacity(n);
let mut next_move = 0;
{
let mut flagcount = 0;
let mut flags = 0;
for _ in 0..n {
if flagcount == 0 {
flags = points[0];
if flags & 8 != 0 {
flagcount = points[1];
points = &points[2..];
} else {
points = &points[1..];
}
} else {
flagcount -= 1;
}
flag_data.push(FlagData { flags, x: 0, y: 0 });
}
}
let mut x_coord = 0_i16;
for flag_data in &mut flag_data {
let flags = flag_data.flags;
if flags & 2 != 0 {
let dx = i16::from(points[0]);
points = &points[1..];
if flags & 16 != 0 {
x_coord += dx;
} else {
x_coord -= dx;
}
} else if flags & 16 == 0 {
x_coord += BE::read_i16(points);
points = &points[2..];
}
flag_data.x = x_coord;
}
let mut y_coord = 0_i16;
for flag_data in &mut flag_data {
let flags = flag_data.flags;
if flags & 4 != 0 {
let dy = i16::from(points[0]);
points = &points[1..];
if flags & 32 != 0 {
y_coord += dy;
} else {
y_coord -= dy;
}
} else if flags & 32 == 0 {
y_coord += BE::read_i16(points);
points = &points[2..];
}
flag_data.y = y_coord;
}
let mut sx = 0;
let mut sy = 0;
let mut cx = 0;
let mut cy = 0;
let mut scx = 0;
let mut scy = 0;
let mut j = 0;
let mut iter = flag_data.into_iter().enumerate().peekable();
while let Some((index, FlagData { flags, x, y })) = iter.next() {
if next_move == index {
if index != 0 {
close_shape(&mut vertices, was_off, start_off, sx, sy, scx, scy, cx, cy);
}
start_off = flags & 1 == 0;
if start_off {
scx = x;
scy = y;
let (next_flags, next_x, next_y) = match iter.peek() {
Some((_, fd)) => (fd.flags, fd.x, fd.y),
None => break,
};
if next_flags & 1 == 0 {
sx = (x + next_x) >> 1;
sy = (y + next_y) >> 1;
} else {
sx = next_x;
sy = next_y;
let _ = iter.next();
}
} else {
sx = x;
sy = y;
}
vertices.push(Vertex {
type_: MoveTo as u8,
x: sx,
y: sy,
cx: 0,
cy: 0,
});
was_off = false;
next_move = 1 + BE::read_u16(&end_points_of_contours[j * 2..]) as usize;
j += 1;
} else if flags & 1 == 0 {
if was_off {
vertices.push(Vertex {
type_: CurveTo as u8,
x: ((cx + x) >> 1),
y: ((cy + y) >> 1),
cx,
cy,
});
}
cx = x;
cy = y;
was_off = true;
} else {
vertices.push(if was_off {
Vertex {
type_: CurveTo as u8,
x,
y,
cx,
cy,
}
} else {
Vertex {
type_: LineTo as u8,
x,
y,
cx: 0,
cy: 0,
}
});
was_off = false;
}
}
close_shape(
&mut vertices,
was_off,
start_off,
sx,
sy,
scx,
scy,
cx,
cy,
);
vertices
}
pub fn get_glyph_h_metrics(&self, glyph_index: u32) -> HMetrics {
let num_of_long_hor_metrics = BE::read_u16(&self.data[self.hhea as usize + 34..]) as usize;
let glyph_index = glyph_index as usize;
if glyph_index < num_of_long_hor_metrics {
let data = &self.data[self.hmtx as usize + 4 * glyph_index..];
let [advance_width, left_side_bearing] = read_ints!(2, i16, data);
HMetrics {
advance_width: i32::from(advance_width),
left_side_bearing: i32::from(left_side_bearing),
}
} else {
HMetrics {
advance_width: BE::read_i16(
&self.data[self.hmtx as usize + 4 * (num_of_long_hor_metrics - 1)..],
) as i32,
left_side_bearing: BE::read_i16(
&self.data[self.hmtx as usize
+ 4 * num_of_long_hor_metrics
+ 2 * (glyph_index as isize - num_of_long_hor_metrics as isize) as usize..],
) as i32,
}
}
}
pub fn get_glyph_kern_advance(&self, glyph_1: u32, glyph_2: u32) -> i32 {
let kern = &self.data[self.kern as usize..];
if self.kern == 0 || BE::read_u16(&kern[2..]) < 1 || BE::read_u16(&kern[8..]) != 1 {
return 0;
}
let mut l: i32 = 0;
let mut r: i32 = BE::read_u16(&kern[10..]) as i32 - 1;
let needle = glyph_1 << 16 | glyph_2;
while l <= r {
let m = (l + r) >> 1;
let straw = BE::read_u32(&kern[18 + (m as usize) * 6..]);
if needle < straw {
r = m - 1;
} else if needle > straw {
l = m + 1;
} else {
return BE::read_i16(&kern[22 + (m as usize) * 6..]) as i32;
}
}
0
}
pub fn get_codepoint_kern_advance(&self, cp1: u32, cp2: u32) -> i32 {
if self.kern == 0 {
0
} else {
self.get_glyph_kern_advance(self.find_glyph_index(cp1), self.find_glyph_index(cp2))
}
}
pub fn get_codepoint_h_metrics(&self, codepoint: u32) -> HMetrics {
self.get_glyph_h_metrics(self.find_glyph_index(codepoint))
}
pub fn get_v_metrics(&self) -> VMetrics {
let hhea = &self.data[self.hhea as usize..];
let [ascent, descent, line_gap] = read_ints!(3, i16, &hhea[4..]);
VMetrics {
ascent: i32::from(ascent),
descent: i32::from(descent),
line_gap: i32::from(line_gap),
}
}
pub fn get_bounding_box(&self) -> Rect<i16> {
let head = &self.data[self.head as usize..];
Rect {
x0: BE::read_i16(&head[36..]),
y0: BE::read_i16(&head[38..]),
x1: BE::read_i16(&head[40..]),
y1: BE::read_i16(&head[42..]),
}
}
pub fn scale_for_pixel_height(&self, height: f32) -> f32 {
let hhea = &self.data[self.hhea as usize..];
let fheight = {
let [a, b] = read_ints!(2, i16, &hhea[4..]);
f32::from(a) - f32::from(b)
};
height / fheight
}
pub fn units_per_em(&self) -> u16 {
BE::read_u16(&self.data[self.head as usize + 18..])
}
pub fn scale_for_mapping_em_to_pixels(&self, pixels: f32) -> f32 {
pixels / (self.units_per_em() as f32)
}
pub fn get_glyph_bitmap_box_subpixel(
&self,
glyph: u32,
scale_x: f32,
scale_y: f32,
shift_x: f32,
shift_y: f32,
) -> Option<Rect<i32>> {
if let Some(glyph_box) = self.get_glyph_box(glyph) {
Some(Rect {
x0: (glyph_box.x0 as f32 * scale_x + shift_x).floor() as i32,
y0: (-glyph_box.y1 as f32 * scale_y + shift_y).floor() as i32,
x1: (glyph_box.x1 as f32 * scale_x + shift_x).ceil() as i32,
y1: (-glyph_box.y0 as f32 * scale_y + shift_y).ceil() as i32,
})
} else {
None
}
}
pub fn get_glyph_bitmap_box(
&self,
glyph: u32,
scale_x: f32,
scale_y: f32,
) -> Option<Rect<i32>> {
self.get_glyph_bitmap_box_subpixel(glyph, scale_x, scale_y, 0.0, 0.0)
}
pub fn get_codepoint_bitmap_box_subpixel(
&self,
codepoint: u32,
scale_x: f32,
scale_y: f32,
shift_x: f32,
shift_y: f32,
) -> Option<Rect<i32>> {
self.get_glyph_bitmap_box_subpixel(
self.find_glyph_index(codepoint),
scale_x,
scale_y,
shift_x,
shift_y,
)
}
pub fn get_codepoint_bitmap_box(
&self,
codepoint: u32,
scale_x: f32,
scale_y: f32,
) -> Option<Rect<i32>> {
self.get_codepoint_bitmap_box_subpixel(codepoint, scale_x, scale_y, 0.0, 0.0)
}
pub fn get_font_name_strings(&self) -> FontNameIter<'_, Data> {
let nm = self.name as usize;
if nm == 0 {
return FontNameIter {
font_info: &self,
string_offset: 0,
index: 0,
count: 0,
};
}
let count = BE::read_u16(&self.data[nm + 2..]) as usize;
let string_offset = nm + BE::read_u16(&self.data[nm + 4..]) as usize;
FontNameIter {
font_info: &self,
string_offset,
index: 0,
count,
}
}
}
#[derive(Clone, Copy, Debug)]
pub struct FontNameIter<'a, Data: Deref<Target = [u8]>> {
font_info: &'a FontInfo<Data>,
string_offset: usize,
index: usize,
count: usize,
}
impl<'a, Data: 'a + Deref<Target = [u8]>> Iterator for FontNameIter<'a, Data> {
type Item = (&'a [u8], Option<PlatformEncodingLanguageId>, u16);
fn next(&mut self) -> Option<Self::Item> {
if self.index >= self.count {
return None;
}
let loc = self.font_info.name as usize + 6 + 12 * self.index;
let pl_id = platform_id(BE::read_u16(&self.font_info.data[loc..]));
let platform_encoding_language_id = pl_id.map(|pl_id| {
let encoding_id = BE::read_u16(&self.font_info.data[loc + 2..]);
let language_id = BE::read_u16(&self.font_info.data[loc + 4..]);
platform_encoding_id(pl_id, Some(encoding_id), Some(language_id))
});
let name_id = BE::read_u16(&self.font_info.data[loc + 6..]);
let length = BE::read_u16(&self.font_info.data[loc + 8..]) as usize;
let offset = self.string_offset + BE::read_u16(&self.font_info.data[loc + 10..]) as usize;
self.index += 1;
Some((
&self.font_info.data[offset..offset + length],
platform_encoding_language_id,
name_id,
))
}
fn size_hint(&self) -> (usize, Option<usize>) {
let remaining = self.count - self.index;
(remaining, Some(remaining))
}
fn count(self) -> usize {
self.count - self.index
}
fn last(mut self) -> Option<Self::Item> {
if self.index >= self.count || self.count == 0 {
return None;
}
self.index = self.count - 1;
self.next()
}
fn nth(&mut self, n: usize) -> Option<Self::Item> {
if n > self.count - self.index {
self.index = self.count;
return None;
}
self.index += n;
self.next()
}
}