use crate::parser::{Parse, ParserContext};
use crate::values::animated::ToAnimatedValue;
use crate::values::computed::{
Angle, Context, Integer, Length, NonNegativeLength, NonNegativeNumber, Number, Percentage,
ToComputedValue, Zoom,
};
use crate::values::generics::font::{
FeatureTagValue, FontSettings, TaggedFontValue, VariationValue,
};
use crate::values::generics::{font as generics, NonNegative};
use crate::values::resolved::{Context as ResolvedContext, ToResolvedValue};
use crate::values::specified::font::{
self as specified, KeywordInfo, MAX_FONT_WEIGHT, MIN_FONT_WEIGHT,
};
use crate::values::specified::length::{FontBaseSize, LineHeightBase, NoCalcLength};
use crate::Atom;
use cssparser::{serialize_identifier, CssStringWriter, Parser};
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
use num_traits::abs;
use num_traits::cast::AsPrimitive;
use std::fmt::{self, Write};
use style_traits::{CssWriter, ParseError, ToCss};
pub use crate::values::computed::Length as MozScriptMinSize;
pub use crate::values::specified::font::MozScriptSizeMultiplier;
pub use crate::values::specified::font::{FontPalette, FontSynthesis};
pub use crate::values::specified::font::{
FontVariantAlternates, FontVariantEastAsian, FontVariantLigatures, FontVariantNumeric, XLang,
XTextScale,
};
pub use crate::values::specified::Integer as SpecifiedInteger;
pub use crate::values::specified::Number as SpecifiedNumber;
#[repr(C)]
#[derive(
Clone,
ComputeSquaredDistance,
Copy,
Debug,
Eq,
Hash,
MallocSizeOf,
PartialEq,
PartialOrd,
ToResolvedValue,
)]
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
pub struct FixedPoint<T, const FRACTION_BITS: u16> {
pub value: T,
}
impl<T, const FRACTION_BITS: u16> FixedPoint<T, FRACTION_BITS>
where
T: AsPrimitive<f32>,
f32: AsPrimitive<T>,
u16: AsPrimitive<T>,
{
const SCALE: u16 = 1 << FRACTION_BITS;
const INVERSE_SCALE: f32 = 1.0 / Self::SCALE as f32;
pub fn from_float(v: f32) -> Self {
Self {
value: (v * Self::SCALE as f32).round().as_(),
}
}
pub fn to_float(&self) -> f32 {
self.value.as_() * Self::INVERSE_SCALE
}
}
impl<const FRACTION_BITS: u16> std::ops::Div for FixedPoint<u16, FRACTION_BITS> {
type Output = Self;
fn div(self, rhs: Self) -> Self {
Self {
value: (((self.value as u32) << (FRACTION_BITS as u32)) / (rhs.value as u32)) as u16,
}
}
}
impl<const FRACTION_BITS: u16> std::ops::Mul for FixedPoint<u16, FRACTION_BITS> {
type Output = Self;
fn mul(self, rhs: Self) -> Self {
Self {
value: (((self.value as u32) * (rhs.value as u32)) >> (FRACTION_BITS as u32)) as u16,
}
}
}
pub const FONT_WEIGHT_FRACTION_BITS: u16 = 6;
pub type FontWeightFixedPoint = FixedPoint<u16, FONT_WEIGHT_FRACTION_BITS>;
#[derive(
Clone,
ComputeSquaredDistance,
Copy,
Debug,
Hash,
MallocSizeOf,
PartialEq,
PartialOrd,
ToResolvedValue,
)]
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
#[repr(C)]
pub struct FontWeight(FontWeightFixedPoint);
impl ToAnimatedValue for FontWeight {
type AnimatedValue = Number;
#[inline]
fn to_animated_value(self, _: &crate::values::animated::Context) -> Self::AnimatedValue {
self.value()
}
#[inline]
fn from_animated_value(animated: Self::AnimatedValue) -> Self {
FontWeight::from_float(animated)
}
}
impl ToCss for FontWeight {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: fmt::Write,
{
self.value().to_css(dest)
}
}
impl FontWeight {
pub const NORMAL: FontWeight = FontWeight(FontWeightFixedPoint {
value: 400 << FONT_WEIGHT_FRACTION_BITS,
});
pub const BOLD: FontWeight = FontWeight(FontWeightFixedPoint {
value: 700 << FONT_WEIGHT_FRACTION_BITS,
});
pub const BOLD_THRESHOLD: FontWeight = FontWeight(FontWeightFixedPoint {
value: 600 << FONT_WEIGHT_FRACTION_BITS,
});
pub fn normal() -> Self {
Self::NORMAL
}
pub fn is_bold(&self) -> bool {
*self >= Self::BOLD_THRESHOLD
}
pub fn value(&self) -> f32 {
self.0.to_float()
}
pub fn from_float(v: f32) -> Self {
Self(FixedPoint::from_float(
v.max(MIN_FONT_WEIGHT).min(MAX_FONT_WEIGHT),
))
}
pub fn bolder(self) -> Self {
let value = self.value();
if value < 350. {
return Self::NORMAL;
}
if value < 550. {
return Self::BOLD;
}
Self::from_float(value.max(900.))
}
pub fn lighter(self) -> Self {
let value = self.value();
if value < 550. {
return Self::from_float(value.min(100.));
}
if value < 750. {
return Self::NORMAL;
}
Self::BOLD
}
}
#[derive(
Animate,
Clone,
ComputeSquaredDistance,
Copy,
Debug,
MallocSizeOf,
PartialEq,
ToAnimatedZero,
ToCss,
)]
#[cfg_attr(feature = "servo", derive(Serialize, Deserialize))]
pub struct FontSize {
pub computed_size: NonNegativeLength,
#[css(skip)]
pub used_size: NonNegativeLength,
#[css(skip)]
pub keyword_info: KeywordInfo,
}
impl FontSize {
#[inline]
pub fn computed_size(&self) -> Length {
self.computed_size.0
}
#[inline]
pub fn used_size(&self) -> Length {
self.used_size.0
}
#[inline]
pub fn zoom(&self, zoom: Zoom) -> Self {
Self {
computed_size: NonNegative(Length::new(zoom.zoom(self.computed_size.0.px()))),
used_size: NonNegative(Length::new(zoom.zoom(self.used_size.0.px()))),
keyword_info: self.keyword_info,
}
}
#[inline]
pub fn medium() -> Self {
Self {
computed_size: NonNegative(Length::new(specified::FONT_MEDIUM_PX)),
used_size: NonNegative(Length::new(specified::FONT_MEDIUM_PX)),
keyword_info: KeywordInfo::medium(),
}
}
}
impl ToAnimatedValue for FontSize {
type AnimatedValue = Length;
#[inline]
fn to_animated_value(self, context: &crate::values::animated::Context) -> Self::AnimatedValue {
self.computed_size.0.to_animated_value(context)
}
#[inline]
fn from_animated_value(animated: Self::AnimatedValue) -> Self {
FontSize {
computed_size: NonNegative(animated.clamp_to_non_negative()),
used_size: NonNegative(animated.clamp_to_non_negative()),
keyword_info: KeywordInfo::none(),
}
}
}
impl ToResolvedValue for FontSize {
type ResolvedValue = NonNegativeLength;
#[inline]
fn to_resolved_value(self, context: &ResolvedContext) -> Self::ResolvedValue {
self.computed_size.to_resolved_value(context)
}
#[inline]
fn from_resolved_value(resolved: Self::ResolvedValue) -> Self {
let computed_size = NonNegativeLength::from_resolved_value(resolved);
Self {
computed_size,
used_size: computed_size,
keyword_info: KeywordInfo::none(),
}
}
}
#[derive(Clone, Debug, Eq, PartialEq, ToComputedValue, ToResolvedValue)]
#[cfg_attr(feature = "servo", derive(Hash, Serialize, Deserialize))]
#[repr(C)]
pub struct FontFamily {
pub families: FontFamilyList,
pub is_system_font: bool,
pub is_initial: bool,
}
macro_rules! static_font_family {
($ident:ident, $family:expr) => {
lazy_static! {
static ref $ident: FontFamily = FontFamily {
families: FontFamilyList {
list: crate::ArcSlice::from_iter_leaked(std::iter::once($family)),
},
is_system_font: false,
is_initial: false,
};
}
};
}
impl FontFamily {
#[inline]
pub fn serif() -> Self {
Self::generic(GenericFontFamily::Serif).clone()
}
#[cfg(feature = "gecko")]
pub(crate) fn moz_bullet() -> &'static Self {
static_font_family!(
MOZ_BULLET,
SingleFontFamily::FamilyName(FamilyName {
name: atom!("-moz-bullet-font"),
syntax: FontFamilyNameSyntax::Identifiers,
})
);
&*MOZ_BULLET
}
#[cfg(feature = "gecko")]
pub fn for_system_font(name: &str) -> Self {
Self {
families: FontFamilyList {
list: crate::ArcSlice::from_iter(std::iter::once(SingleFontFamily::FamilyName(
FamilyName {
name: Atom::from(name),
syntax: FontFamilyNameSyntax::Identifiers,
},
))),
},
is_system_font: true,
is_initial: false,
}
}
pub fn generic(generic: GenericFontFamily) -> &'static Self {
macro_rules! generic_font_family {
($ident:ident, $family:ident) => {
static_font_family!(
$ident,
SingleFontFamily::Generic(GenericFontFamily::$family)
)
};
}
generic_font_family!(SERIF, Serif);
generic_font_family!(SANS_SERIF, SansSerif);
generic_font_family!(MONOSPACE, Monospace);
generic_font_family!(CURSIVE, Cursive);
generic_font_family!(FANTASY, Fantasy);
#[cfg(feature = "gecko")]
generic_font_family!(MOZ_EMOJI, MozEmoji);
generic_font_family!(SYSTEM_UI, SystemUi);
let family = match generic {
GenericFontFamily::None => {
debug_assert!(false, "Bogus caller!");
&*SERIF
},
GenericFontFamily::Serif => &*SERIF,
GenericFontFamily::SansSerif => &*SANS_SERIF,
GenericFontFamily::Monospace => &*MONOSPACE,
GenericFontFamily::Cursive => &*CURSIVE,
GenericFontFamily::Fantasy => &*FANTASY,
#[cfg(feature = "gecko")]
GenericFontFamily::MozEmoji => &*MOZ_EMOJI,
GenericFontFamily::SystemUi => &*SYSTEM_UI,
};
debug_assert_eq!(
*family.families.iter().next().unwrap(),
SingleFontFamily::Generic(generic)
);
family
}
}
impl MallocSizeOf for FontFamily {
fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
use malloc_size_of::MallocUnconditionalSizeOf;
let shared_font_list = &self.families.list;
if shared_font_list.is_unique() {
shared_font_list.unconditional_size_of(ops)
} else {
0
}
}
}
impl ToCss for FontFamily {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: fmt::Write,
{
let mut iter = self.families.iter();
match iter.next() {
Some(f) => f.to_css(dest)?,
None => return Ok(()),
}
for family in iter {
dest.write_str(", ")?;
family.to_css(dest)?;
}
Ok(())
}
}
#[derive(
Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem,
)]
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
#[repr(C)]
pub struct FamilyName {
pub name: Atom,
pub syntax: FontFamilyNameSyntax,
}
#[cfg(feature = "gecko")]
impl FamilyName {
fn is_known_icon_font_family(&self) -> bool {
use crate::gecko_bindings::bindings;
unsafe { bindings::Gecko_IsKnownIconFontFamily(self.name.as_ptr()) }
}
}
#[cfg(feature = "servo")]
impl FamilyName {
fn is_known_icon_font_family(&self) -> bool {
false
}
}
impl ToCss for FamilyName {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: fmt::Write,
{
match self.syntax {
FontFamilyNameSyntax::Quoted => {
dest.write_char('"')?;
write!(CssStringWriter::new(dest), "{}", self.name)?;
dest.write_char('"')
},
FontFamilyNameSyntax::Identifiers => {
let mut first = true;
for ident in self.name.to_string().split(' ') {
if first {
first = false;
} else {
dest.write_char(' ')?;
}
debug_assert!(
!ident.is_empty(),
"Family name with leading, \
trailing, or consecutive white spaces should \
have been marked quoted by the parser"
);
serialize_identifier(ident, dest)?;
}
Ok(())
},
}
}
}
#[derive(
Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem,
)]
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
#[repr(u8)]
pub enum FontFamilyNameSyntax {
Quoted,
Identifiers,
}
#[derive(
Clone, Debug, Eq, MallocSizeOf, PartialEq, ToCss, ToComputedValue, ToResolvedValue, ToShmem,
)]
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize, Hash))]
#[repr(u8)]
pub enum SingleFontFamily {
FamilyName(FamilyName),
Generic(GenericFontFamily),
}
fn system_ui_enabled(_: &ParserContext) -> bool {
static_prefs::pref!("layout.css.system-ui.enabled")
}
#[derive(
Clone,
Copy,
Debug,
Eq,
Hash,
MallocSizeOf,
PartialEq,
Parse,
ToCss,
ToComputedValue,
ToResolvedValue,
ToShmem,
)]
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
#[repr(u32)]
#[allow(missing_docs)]
pub enum GenericFontFamily {
#[css(skip)]
None = 0,
Serif,
SansSerif,
#[parse(aliases = "-moz-fixed")]
Monospace,
Cursive,
Fantasy,
#[parse(condition = "system_ui_enabled")]
SystemUi,
#[css(skip)]
#[cfg(feature = "gecko")]
MozEmoji,
}
impl GenericFontFamily {
pub(crate) fn valid_for_user_font_prioritization(self) -> bool {
match self {
Self::None | Self::Fantasy | Self::Cursive | Self::SystemUi => false,
#[cfg(feature = "gecko")]
Self::MozEmoji => false,
Self::Serif | Self::SansSerif | Self::Monospace => true,
}
}
}
impl Parse for SingleFontFamily {
fn parse<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> {
if let Ok(value) = input.try_parse(|i| i.expect_string_cloned()) {
return Ok(SingleFontFamily::FamilyName(FamilyName {
name: Atom::from(&*value),
syntax: FontFamilyNameSyntax::Quoted,
}));
}
if let Ok(generic) = input.try_parse(|i| GenericFontFamily::parse(context, i)) {
return Ok(SingleFontFamily::Generic(generic));
}
let first_ident = input.expect_ident_cloned()?;
let reserved = match_ignore_ascii_case! { &first_ident,
"inherit" | "initial" | "unset" | "revert" | "default" => true,
_ => false,
};
let mut value = first_ident.as_ref().to_owned();
let mut serialize_quoted = value.contains(' ');
if reserved {
let ident = input.expect_ident()?;
serialize_quoted = serialize_quoted || ident.contains(' ');
value.push(' ');
value.push_str(&ident);
}
while let Ok(ident) = input.try_parse(|i| i.expect_ident_cloned()) {
serialize_quoted = serialize_quoted || ident.contains(' ');
value.push(' ');
value.push_str(&ident);
}
let syntax = if serialize_quoted {
FontFamilyNameSyntax::Quoted
} else {
FontFamilyNameSyntax::Identifiers
};
Ok(SingleFontFamily::FamilyName(FamilyName {
name: Atom::from(value),
syntax,
}))
}
}
#[derive(Clone, Debug, ToComputedValue, ToResolvedValue, ToShmem, PartialEq, Eq)]
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize, Hash))]
#[repr(C)]
pub struct FontFamilyList {
pub list: crate::ArcSlice<SingleFontFamily>,
}
impl FontFamilyList {
pub fn iter(&self) -> impl Iterator<Item = &SingleFontFamily> {
self.list.iter()
}
#[cfg_attr(feature = "servo", allow(unused))]
pub(crate) fn prioritize_first_generic_or_prepend(&mut self, generic: GenericFontFamily) {
let mut index_of_first_generic = None;
let mut target_index = None;
for (i, f) in self.iter().enumerate() {
match &*f {
SingleFontFamily::Generic(f) => {
if index_of_first_generic.is_none() && f.valid_for_user_font_prioritization() {
if target_index.is_none() {
return;
}
index_of_first_generic = Some(i);
break;
}
if target_index.is_none() {
target_index = Some(i);
}
},
SingleFontFamily::FamilyName(fam) => {
if target_index.is_none() && !fam.is_known_icon_font_family() {
target_index = Some(i);
}
},
}
}
let mut new_list = self.list.iter().cloned().collect::<Vec<_>>();
let first_generic = match index_of_first_generic {
Some(i) => new_list.remove(i),
None => SingleFontFamily::Generic(generic),
};
if let Some(i) = target_index {
new_list.insert(i, first_generic);
} else {
new_list.push(first_generic);
}
self.list = crate::ArcSlice::from_iter(new_list.into_iter());
}
#[cfg_attr(feature = "servo", allow(unused))]
pub(crate) fn needs_user_font_prioritization(&self) -> bool {
self.iter().next().map_or(true, |f| match f {
SingleFontFamily::Generic(f) => !f.valid_for_user_font_prioritization(),
_ => true,
})
}
pub fn single_generic(&self) -> Option<GenericFontFamily> {
let mut iter = self.iter();
if let Some(SingleFontFamily::Generic(f)) = iter.next() {
if iter.next().is_none() {
return Some(*f);
}
}
None
}
}
pub type FontSizeAdjust = generics::GenericFontSizeAdjust<NonNegativeNumber>;
impl FontSizeAdjust {
#[inline]
pub fn none() -> Self {
FontSizeAdjust::None
}
}
impl ToComputedValue for specified::FontSizeAdjust {
type ComputedValue = FontSizeAdjust;
fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
use crate::font_metrics::FontMetricsOrientation;
let font_metrics = |vertical| {
let orient = if vertical {
FontMetricsOrientation::MatchContextPreferVertical
} else {
FontMetricsOrientation::Horizontal
};
let metrics = context.query_font_metrics(FontBaseSize::CurrentStyle, orient, false);
let font_size = context.style().get_font().clone_font_size().used_size.0;
(metrics, font_size)
};
macro_rules! resolve {
($basis:ident, $value:expr, $vertical:expr, $field:ident, $fallback:expr) => {{
match $value {
specified::FontSizeAdjustFactor::Number(f) => {
FontSizeAdjust::$basis(f.to_computed_value(context))
},
specified::FontSizeAdjustFactor::FromFont => {
let (metrics, font_size) = font_metrics($vertical);
let ratio = if let Some(metric) = metrics.$field {
metric / font_size
} else if $fallback >= 0.0 {
$fallback
} else {
metrics.ascent / font_size
};
if ratio.is_nan() {
FontSizeAdjust::$basis(NonNegative(abs($fallback)))
} else {
FontSizeAdjust::$basis(NonNegative(ratio))
}
},
}
}};
}
match *self {
Self::None => FontSizeAdjust::None,
Self::ExHeight(val) => resolve!(ExHeight, val, false, x_height, 0.5),
Self::CapHeight(val) => {
resolve!(CapHeight, val, false, cap_height, -1.0 )
},
Self::ChWidth(val) => resolve!(ChWidth, val, false, zero_advance_measure, 0.5),
Self::IcWidth(val) => resolve!(IcWidth, val, false, ic_width, 1.0),
Self::IcHeight(val) => resolve!(IcHeight, val, true, ic_width, 1.0),
}
}
fn from_computed_value(computed: &Self::ComputedValue) -> Self {
macro_rules! case {
($basis:ident, $val:expr) => {
Self::$basis(specified::FontSizeAdjustFactor::Number(
ToComputedValue::from_computed_value($val),
))
};
}
match *computed {
FontSizeAdjust::None => Self::None,
FontSizeAdjust::ExHeight(ref val) => case!(ExHeight, val),
FontSizeAdjust::CapHeight(ref val) => case!(CapHeight, val),
FontSizeAdjust::ChWidth(ref val) => case!(ChWidth, val),
FontSizeAdjust::IcWidth(ref val) => case!(IcWidth, val),
FontSizeAdjust::IcHeight(ref val) => case!(IcHeight, val),
}
}
}
pub type FontFeatureSettings = FontSettings<FeatureTagValue<Integer>>;
pub type FontVariationSettings = FontSettings<VariationValue<Number>>;
fn dedup_font_settings<T>(settings_list: &mut Vec<T>)
where
T: TaggedFontValue,
{
if settings_list.len() > 1 {
settings_list.sort_by_key(|k| k.tag().0);
let mut prev_tag = settings_list.last().unwrap().tag();
for i in (0..settings_list.len() - 1).rev() {
let cur_tag = settings_list[i].tag();
if cur_tag == prev_tag {
settings_list.remove(i);
}
prev_tag = cur_tag;
}
}
}
impl<T> ToComputedValue for FontSettings<T>
where
T: ToComputedValue,
<T as ToComputedValue>::ComputedValue: TaggedFontValue,
{
type ComputedValue = FontSettings<T::ComputedValue>;
fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
let mut v = self
.0
.iter()
.map(|item| item.to_computed_value(context))
.collect::<Vec<_>>();
dedup_font_settings(&mut v);
FontSettings(v.into_boxed_slice())
}
fn from_computed_value(computed: &Self::ComputedValue) -> Self {
Self(
computed
.0
.iter()
.map(T::from_computed_value)
.collect()
)
}
}
#[derive(
Clone,
Copy,
Debug,
Eq,
MallocSizeOf,
PartialEq,
SpecifiedValueInfo,
ToComputedValue,
ToResolvedValue,
ToShmem,
)]
#[repr(C)]
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
#[value_info(other_values = "normal")]
pub struct FontLanguageOverride(pub u32);
impl FontLanguageOverride {
#[inline]
pub fn normal() -> FontLanguageOverride {
FontLanguageOverride(0)
}
#[inline]
pub(crate) fn to_str(self, storage: &mut [u8; 4]) -> &str {
*storage = u32::to_be_bytes(self.0);
let slice = if cfg!(debug_assertions) {
std::str::from_utf8(&storage[..]).unwrap()
} else {
unsafe { std::str::from_utf8_unchecked(&storage[..]) }
};
slice.trim_end()
}
#[inline]
pub unsafe fn from_u32(value: u32) -> Self {
Self(value)
}
}
impl ToCss for FontLanguageOverride {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: fmt::Write,
{
if self.0 == 0 {
return dest.write_str("normal");
}
self.to_str(&mut [0; 4]).to_css(dest)
}
}
impl ToComputedValue for specified::MozScriptMinSize {
type ComputedValue = MozScriptMinSize;
fn to_computed_value(&self, cx: &Context) -> MozScriptMinSize {
let base_size = FontBaseSize::InheritedStyle;
let line_height_base = LineHeightBase::InheritedStyle;
match self.0 {
NoCalcLength::FontRelative(value) => {
value.to_computed_value(cx, base_size, line_height_base)
},
NoCalcLength::ServoCharacterWidth(value) => {
value.to_computed_value(base_size.resolve(cx).computed_size())
},
ref l => l.to_computed_value(cx),
}
}
fn from_computed_value(other: &MozScriptMinSize) -> Self {
specified::MozScriptMinSize(ToComputedValue::from_computed_value(other))
}
}
pub type MathDepth = i8;
#[cfg(feature = "gecko")]
impl ToComputedValue for specified::MathDepth {
type ComputedValue = MathDepth;
fn to_computed_value(&self, cx: &Context) -> i8 {
use crate::properties::longhands::math_style::SpecifiedValue as MathStyleValue;
use std::{cmp, i8};
let int = match *self {
specified::MathDepth::AutoAdd => {
let parent = cx.builder.get_parent_font().clone_math_depth() as i32;
let style = cx.builder.get_parent_font().clone_math_style();
if style == MathStyleValue::Compact {
parent.saturating_add(1)
} else {
parent
}
},
specified::MathDepth::Add(rel) => {
let parent = cx.builder.get_parent_font().clone_math_depth();
(parent as i32).saturating_add(rel.to_computed_value(cx))
},
specified::MathDepth::Absolute(abs) => abs.to_computed_value(cx),
};
cmp::min(int, i8::MAX as i32) as i8
}
fn from_computed_value(other: &i8) -> Self {
let computed_value = *other as i32;
specified::MathDepth::Absolute(SpecifiedInteger::from_computed_value(&computed_value))
}
}
pub const FONT_STYLE_FRACTION_BITS: u16 = 8;
pub type FontStyleFixedPoint = FixedPoint<i16, FONT_STYLE_FRACTION_BITS>;
#[derive(
Clone,
ComputeSquaredDistance,
Copy,
Debug,
Eq,
Hash,
MallocSizeOf,
PartialEq,
PartialOrd,
ToResolvedValue,
)]
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
#[repr(C)]
pub struct FontStyle(FontStyleFixedPoint);
impl FontStyle {
pub const NORMAL: FontStyle = FontStyle(FontStyleFixedPoint {
value: 100 << FONT_STYLE_FRACTION_BITS,
});
pub const ITALIC: FontStyle = FontStyle(FontStyleFixedPoint {
value: 101 << FONT_STYLE_FRACTION_BITS,
});
pub const DEFAULT_OBLIQUE_DEGREES: i16 = 14;
pub const OBLIQUE: FontStyle = FontStyle(FontStyleFixedPoint {
value: Self::DEFAULT_OBLIQUE_DEGREES << FONT_STYLE_FRACTION_BITS,
});
#[inline]
pub fn normal() -> Self {
Self::NORMAL
}
pub fn oblique(degrees: f32) -> Self {
Self(FixedPoint::from_float(
degrees
.max(specified::FONT_STYLE_OBLIQUE_MIN_ANGLE_DEGREES)
.min(specified::FONT_STYLE_OBLIQUE_MAX_ANGLE_DEGREES),
))
}
pub fn oblique_degrees(&self) -> f32 {
debug_assert_ne!(*self, Self::NORMAL);
debug_assert_ne!(*self, Self::ITALIC);
self.0.to_float()
}
}
impl ToCss for FontStyle {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: fmt::Write,
{
if *self == Self::NORMAL {
return dest.write_str("normal");
}
if *self == Self::ITALIC {
return dest.write_str("italic");
}
if *self == Self::OBLIQUE {
return dest.write_str("oblique");
}
dest.write_str("oblique ")?;
let angle = Angle::from_degrees(self.oblique_degrees());
angle.to_css(dest)?;
Ok(())
}
}
impl ToAnimatedValue for FontStyle {
type AnimatedValue = generics::FontStyle<Angle>;
#[inline]
fn to_animated_value(self, _: &crate::values::animated::Context) -> Self::AnimatedValue {
if self == Self::NORMAL {
return generics::FontStyle::Oblique(Angle::from_degrees(0.0));
}
if self == Self::ITALIC {
return generics::FontStyle::Italic;
}
generics::FontStyle::Oblique(Angle::from_degrees(self.oblique_degrees()))
}
#[inline]
fn from_animated_value(animated: Self::AnimatedValue) -> Self {
match animated {
generics::FontStyle::Normal => Self::NORMAL,
generics::FontStyle::Italic => Self::ITALIC,
generics::FontStyle::Oblique(ref angle) => {
if angle.degrees() == 0.0 {
Self::NORMAL
} else {
Self::oblique(angle.degrees())
}
},
}
}
}
pub const FONT_STRETCH_FRACTION_BITS: u16 = 6;
pub type FontStretchFixedPoint = FixedPoint<u16, FONT_STRETCH_FRACTION_BITS>;
#[derive(
Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf, PartialEq, PartialOrd, ToResolvedValue,
)]
#[cfg_attr(feature = "servo", derive(Deserialize, Hash, Serialize))]
#[repr(C)]
pub struct FontStretch(pub FontStretchFixedPoint);
impl FontStretch {
pub const FRACTION_BITS: u16 = FONT_STRETCH_FRACTION_BITS;
pub const HALF: u16 = 1 << (Self::FRACTION_BITS - 1);
pub const ULTRA_CONDENSED: FontStretch = FontStretch(FontStretchFixedPoint {
value: 50 << Self::FRACTION_BITS,
});
pub const EXTRA_CONDENSED: FontStretch = FontStretch(FontStretchFixedPoint {
value: (62 << Self::FRACTION_BITS) + Self::HALF,
});
pub const CONDENSED: FontStretch = FontStretch(FontStretchFixedPoint {
value: 75 << Self::FRACTION_BITS,
});
pub const SEMI_CONDENSED: FontStretch = FontStretch(FontStretchFixedPoint {
value: (87 << Self::FRACTION_BITS) + Self::HALF,
});
pub const NORMAL: FontStretch = FontStretch(FontStretchFixedPoint {
value: 100 << Self::FRACTION_BITS,
});
pub const SEMI_EXPANDED: FontStretch = FontStretch(FontStretchFixedPoint {
value: (112 << Self::FRACTION_BITS) + Self::HALF,
});
pub const EXPANDED: FontStretch = FontStretch(FontStretchFixedPoint {
value: 125 << Self::FRACTION_BITS,
});
pub const EXTRA_EXPANDED: FontStretch = FontStretch(FontStretchFixedPoint {
value: 150 << Self::FRACTION_BITS,
});
pub const ULTRA_EXPANDED: FontStretch = FontStretch(FontStretchFixedPoint {
value: 200 << Self::FRACTION_BITS,
});
pub fn hundred() -> Self {
Self::NORMAL
}
#[inline]
pub fn to_percentage(&self) -> Percentage {
Percentage(self.0.to_float() / 100.0)
}
pub fn from_percentage(p: f32) -> Self {
Self(FixedPoint::from_float((p * 100.).max(0.0).min(1000.0)))
}
pub fn from_keyword(kw: specified::FontStretchKeyword) -> Self {
use specified::FontStretchKeyword::*;
match kw {
UltraCondensed => Self::ULTRA_CONDENSED,
ExtraCondensed => Self::EXTRA_CONDENSED,
Condensed => Self::CONDENSED,
SemiCondensed => Self::SEMI_CONDENSED,
Normal => Self::NORMAL,
SemiExpanded => Self::SEMI_EXPANDED,
Expanded => Self::EXPANDED,
ExtraExpanded => Self::EXTRA_EXPANDED,
UltraExpanded => Self::ULTRA_EXPANDED,
}
}
pub fn as_keyword(&self) -> Option<specified::FontStretchKeyword> {
use specified::FontStretchKeyword::*;
if *self == Self::ULTRA_CONDENSED {
return Some(UltraCondensed);
}
if *self == Self::EXTRA_CONDENSED {
return Some(ExtraCondensed);
}
if *self == Self::CONDENSED {
return Some(Condensed);
}
if *self == Self::SEMI_CONDENSED {
return Some(SemiCondensed);
}
if *self == Self::NORMAL {
return Some(Normal);
}
if *self == Self::SEMI_EXPANDED {
return Some(SemiExpanded);
}
if *self == Self::EXPANDED {
return Some(Expanded);
}
if *self == Self::EXTRA_EXPANDED {
return Some(ExtraExpanded);
}
if *self == Self::ULTRA_EXPANDED {
return Some(UltraExpanded);
}
None
}
}
impl ToCss for FontStretch {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: fmt::Write,
{
self.to_percentage().to_css(dest)
}
}
impl ToAnimatedValue for FontStretch {
type AnimatedValue = Percentage;
#[inline]
fn to_animated_value(self, _: &crate::values::animated::Context) -> Self::AnimatedValue {
self.to_percentage()
}
#[inline]
fn from_animated_value(animated: Self::AnimatedValue) -> Self {
Self::from_percentage(animated.0)
}
}
pub type LineHeight = generics::GenericLineHeight<NonNegativeNumber, NonNegativeLength>;
impl ToResolvedValue for LineHeight {
type ResolvedValue = Self;
fn to_resolved_value(self, context: &ResolvedContext) -> Self::ResolvedValue {
#[cfg(feature = "gecko")]
{
if matches!(self, Self::Normal | Self::MozBlockHeight) {
return self;
}
let wm = context.style.writing_mode;
Self::Length(
context
.device
.calc_line_height(
context.style.get_font(),
wm,
Some(context.element_info.element),
)
.to_resolved_value(context),
)
}
#[cfg(feature = "servo")]
{
if let LineHeight::Number(num) = &self {
let size = context.style.get_font().clone_font_size().computed_size();
LineHeight::Length(NonNegativeLength::new(size.px() * num.0))
} else {
self
}
}
}
#[inline]
fn from_resolved_value(value: Self::ResolvedValue) -> Self {
value
}
}