use crate::parser::Stream;
use crate::{Font, LineMetrics};
macro_rules! try_opt_or {
($value:expr, $ret:expr) => {
match $value {
Some(v) => v,
None => return $ret,
}
};
}
#[derive(Clone, Copy, PartialEq, Debug)]
#[allow(missing_docs)]
pub enum Weight {
Thin,
ExtraLight,
Light,
Normal,
Medium,
SemiBold,
Bold,
ExtraBold,
Black,
Other(u16),
}
impl Weight {
#[inline]
pub fn to_number(&self) -> u16 {
match self {
Weight::Thin => 100,
Weight::ExtraLight => 200,
Weight::Light => 300,
Weight::Normal => 400,
Weight::Medium => 500,
Weight::SemiBold => 600,
Weight::Bold => 700,
Weight::ExtraBold => 800,
Weight::Black => 900,
Weight::Other(n) => *n,
}
}
}
impl From<u16> for Weight {
#[inline]
fn from(value: u16) -> Self {
match value {
100 => Weight::Thin,
200 => Weight::ExtraLight,
300 => Weight::Light,
400 => Weight::Normal,
500 => Weight::Medium,
600 => Weight::SemiBold,
700 => Weight::Bold,
800 => Weight::ExtraBold,
900 => Weight::Black,
_ => Weight::Other(value),
}
}
}
impl Default for Weight {
#[inline]
fn default() -> Self {
Weight::Normal
}
}
#[derive(Clone, Copy, PartialEq, PartialOrd, Debug)]
#[allow(missing_docs)]
pub enum Width {
UltraCondensed,
ExtraCondensed,
Condensed,
SemiCondensed,
Normal,
SemiExpanded,
Expanded,
ExtraExpanded,
UltraExpanded,
}
impl Width {
#[inline]
pub fn to_number(&self) -> u16 {
match self {
Width::UltraCondensed => 1,
Width::ExtraCondensed => 2,
Width::Condensed => 3,
Width::SemiCondensed => 4,
Width::Normal => 5,
Width::SemiExpanded => 6,
Width::Expanded => 7,
Width::ExtraExpanded => 8,
Width::UltraExpanded => 9,
}
}
}
impl Default for Width {
#[inline]
fn default() -> Self {
Width::Normal
}
}
#[derive(Clone, Copy, PartialEq, Debug)]
pub struct ScriptMetrics {
pub x_size: i16,
pub y_size: i16,
pub x_offset: i16,
pub y_offset: i16,
}
impl<'a> Font<'a> {
#[inline]
pub fn weight(&self) -> Weight {
let table = try_opt_or!(self.os_2_v0, Weight::default());
Weight::from(table.us_weight_class())
}
#[inline]
pub fn width(&self) -> Width {
let table = try_opt_or!(self.os_2_v0, Width::default());
match table.us_width_class() {
1 => Width::UltraCondensed,
2 => Width::ExtraCondensed,
3 => Width::Condensed,
4 => Width::SemiCondensed,
5 => Width::Normal,
6 => Width::SemiExpanded,
7 => Width::Expanded,
8 => Width::ExtraExpanded,
9 => Width::UltraExpanded,
_ => Width::Normal,
}
}
#[inline]
pub fn is_regular(&self) -> bool {
const REGULAR_FLAG: u16 = 6;
self.get_fs_selection(REGULAR_FLAG)
}
#[inline]
pub fn is_italic(&self) -> bool {
const ITALIC_FLAG: u16 = 0;
self.get_fs_selection(ITALIC_FLAG)
}
#[inline]
pub fn is_bold(&self) -> bool {
const BOLD_FLAG: u16 = 5;
self.get_fs_selection(BOLD_FLAG)
}
#[inline]
pub fn is_oblique(&self) -> bool {
let table = try_opt_or!(self.os_2_v0, false);
if table.version() < 4 {
return false;
}
const OBLIQUE_FLAG: u16 = 9;
self.get_fs_selection(OBLIQUE_FLAG)
}
#[inline]
fn get_fs_selection(&self, bit: u16) -> bool {
let table = try_opt_or!(self.os_2_v0, false);
(table.fs_selection() >> bit) & 1 == 1
}
#[inline]
pub fn x_height(&self) -> Option<i16> {
const SX_HEIGHT_OFFSET: usize = 86;
let table = self.os_2_v0?;
if table.version() < 2 {
return None;
}
Stream::read_at(self.os_2?, SX_HEIGHT_OFFSET).ok()
}
#[inline]
pub fn strikeout_metrics(&self) -> Option<LineMetrics> {
let table = self.os_2_v0?;
Some(LineMetrics {
thickness: table.y_strikeout_size(),
position: table.y_strikeout_position(),
})
}
#[inline]
pub fn subscript_metrics(&self) -> Option<ScriptMetrics> {
let table = self.os_2_v0?;
Some(ScriptMetrics {
x_size: table.y_subscript_x_size(),
y_size: table.y_subscript_y_size(),
x_offset: table.y_subscript_x_offset(),
y_offset: table.y_subscript_y_offset(),
})
}
#[inline]
pub fn superscript_metrics(&self) -> Option<ScriptMetrics> {
let table = self.os_2_v0?;
Some(ScriptMetrics {
x_size: table.y_superscript_x_size(),
y_size: table.y_superscript_y_size(),
x_offset: table.y_superscript_x_offset(),
y_offset: table.y_superscript_y_offset(),
})
}
}