mirror of
https://github.com/servo/servo.git
synced 2025-07-01 04:23:39 +01:00
969 lines
31 KiB
Rust
969 lines
31 KiB
Rust
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||
|
||
//! Computed values for font properties
|
||
|
||
use Atom;
|
||
use app_units::Au;
|
||
use byteorder::{BigEndian, ByteOrder};
|
||
use cssparser::{serialize_identifier, CssStringWriter, Parser};
|
||
#[cfg(feature = "gecko")]
|
||
use gecko_bindings::{bindings, structs};
|
||
#[cfg(feature = "gecko")]
|
||
use gecko_bindings::sugar::refptr::RefPtr;
|
||
#[cfg(feature = "gecko")]
|
||
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
|
||
use std::fmt::{self, Write};
|
||
use std::hash::{Hash, Hasher};
|
||
#[cfg(feature = "servo")]
|
||
use std::slice;
|
||
use style_traits::{CssWriter, ParseError, ToCss};
|
||
use values::CSSFloat;
|
||
use values::animated::{ToAnimatedValue, ToAnimatedZero};
|
||
use values::computed::{Angle, Context, Integer, NonNegativeLength, NonNegativePercentage};
|
||
use values::computed::{Number, Percentage, ToComputedValue};
|
||
use values::generics::font::{self as generics, FeatureTagValue, FontSettings, VariationValue};
|
||
use values::specified::font::{self as specified, MIN_FONT_WEIGHT, MAX_FONT_WEIGHT};
|
||
use values::specified::length::{FontBaseSize, NoCalcLength};
|
||
|
||
pub use values::computed::Length as MozScriptMinSize;
|
||
pub use values::specified::font::{FontSynthesis, MozScriptSizeMultiplier, XLang, XTextZoom};
|
||
|
||
/// A value for the font-weight property per:
|
||
///
|
||
/// https://drafts.csswg.org/css-fonts-4/#propdef-font-weight
|
||
///
|
||
/// This is effectively just a `Number`.
|
||
#[derive(Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf, PartialEq,
|
||
ToCss)]
|
||
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
|
||
pub struct FontWeight(pub Number);
|
||
|
||
impl Hash for FontWeight {
|
||
fn hash<H: Hasher>(&self, hasher: &mut H) {
|
||
hasher.write_u64((self.0 * 10000.).trunc() as u64);
|
||
}
|
||
}
|
||
|
||
impl ToAnimatedValue for FontWeight {
|
||
type AnimatedValue = Number;
|
||
|
||
#[inline]
|
||
fn to_animated_value(self) -> Self::AnimatedValue {
|
||
self.0
|
||
}
|
||
|
||
#[inline]
|
||
fn from_animated_value(animated: Self::AnimatedValue) -> Self {
|
||
FontWeight(animated.max(MIN_FONT_WEIGHT).min(MAX_FONT_WEIGHT))
|
||
}
|
||
}
|
||
|
||
#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf, PartialEq,
|
||
ToAnimatedZero, ToCss)]
|
||
/// The computed value of font-size
|
||
pub struct FontSize {
|
||
/// The size.
|
||
pub size: NonNegativeLength,
|
||
/// If derived from a keyword, the keyword and additional transformations applied to it
|
||
#[css(skip)]
|
||
pub keyword_info: Option<KeywordInfo>,
|
||
}
|
||
|
||
/// Additional information for computed keyword-derived font sizes.
|
||
pub type KeywordInfo = generics::KeywordInfo<NonNegativeLength>;
|
||
|
||
impl FontWeight {
|
||
/// Value for normal
|
||
pub fn normal() -> Self {
|
||
FontWeight(400.)
|
||
}
|
||
|
||
/// Value for bold
|
||
pub fn bold() -> Self {
|
||
FontWeight(700.)
|
||
}
|
||
|
||
/// Convert from an Gecko weight
|
||
#[cfg(feature = "gecko")]
|
||
pub fn from_gecko_weight(weight: structs::FontWeight) -> Self {
|
||
// we allow a wider range of weights than is parseable
|
||
// because system fonts may provide custom values
|
||
let weight = unsafe { bindings::Gecko_FontWeight_ToFloat(weight) };
|
||
FontWeight(weight)
|
||
}
|
||
|
||
/// Weither this weight is bold
|
||
pub fn is_bold(&self) -> bool {
|
||
self.0 > 500.
|
||
}
|
||
|
||
/// Return the bolder weight.
|
||
///
|
||
/// See the table in:
|
||
/// https://drafts.csswg.org/css-fonts-4/#font-weight-numeric-values
|
||
pub fn bolder(self) -> Self {
|
||
if self.0 < 350. {
|
||
FontWeight(400.)
|
||
} else if self.0 < 550. {
|
||
FontWeight(700.)
|
||
} else {
|
||
FontWeight(self.0.max(900.))
|
||
}
|
||
}
|
||
|
||
/// Return the lighter weight.
|
||
///
|
||
/// See the table in:
|
||
/// https://drafts.csswg.org/css-fonts-4/#font-weight-numeric-values
|
||
pub fn lighter(self) -> Self {
|
||
if self.0 < 550. {
|
||
FontWeight(self.0.min(100.))
|
||
} else if self.0 < 750. {
|
||
FontWeight(400.)
|
||
} else {
|
||
FontWeight(700.)
|
||
}
|
||
}
|
||
}
|
||
|
||
impl FontSize {
|
||
/// The actual computed font size.
|
||
pub fn size(self) -> Au {
|
||
self.size.into()
|
||
}
|
||
|
||
#[inline]
|
||
/// Get default value of font size.
|
||
pub fn medium() -> Self {
|
||
Self {
|
||
size: Au::from_px(specified::FONT_MEDIUM_PX).into(),
|
||
keyword_info: Some(KeywordInfo::medium()),
|
||
}
|
||
}
|
||
|
||
/// FIXME(emilio): This is very complex. Also, it should move to
|
||
/// StyleBuilder.
|
||
pub fn cascade_inherit_font_size(context: &mut Context) {
|
||
// If inheriting, we must recompute font-size in case of language
|
||
// changes using the font_size_keyword. We also need to do this to
|
||
// handle mathml scriptlevel changes
|
||
let kw_inherited_size = context
|
||
.builder
|
||
.get_parent_font()
|
||
.clone_font_size()
|
||
.keyword_info
|
||
.map(|info| {
|
||
specified::FontSize::Keyword(info)
|
||
.to_computed_value(context)
|
||
.size
|
||
});
|
||
let mut font = context.builder.take_font();
|
||
font.inherit_font_size_from(
|
||
context.builder.get_parent_font(),
|
||
kw_inherited_size,
|
||
context.builder.device,
|
||
);
|
||
context.builder.put_font(font);
|
||
}
|
||
|
||
/// Cascade the initial value for the `font-size` property.
|
||
///
|
||
/// FIXME(emilio): This is the only function that is outside of the
|
||
/// `StyleBuilder`, and should really move inside!
|
||
///
|
||
/// Can we move the font stuff there?
|
||
pub fn cascade_initial_font_size(context: &mut Context) {
|
||
// font-size's default ("medium") does not always
|
||
// compute to the same value and depends on the font
|
||
let computed = specified::FontSize::medium().to_computed_value(context);
|
||
context.builder.mutate_font().set_font_size(computed);
|
||
#[cfg(feature = "gecko")]
|
||
{
|
||
let device = context.builder.device;
|
||
context.builder.mutate_font().fixup_font_min_size(device);
|
||
}
|
||
}
|
||
}
|
||
|
||
/// XXXManishearth it might be better to
|
||
/// animate this as computed, however this complicates
|
||
/// clamping and might not be the right thing to do.
|
||
/// We should figure it out.
|
||
impl ToAnimatedValue for FontSize {
|
||
type AnimatedValue = NonNegativeLength;
|
||
|
||
#[inline]
|
||
fn to_animated_value(self) -> Self::AnimatedValue {
|
||
self.size
|
||
}
|
||
|
||
#[inline]
|
||
fn from_animated_value(animated: Self::AnimatedValue) -> Self {
|
||
FontSize {
|
||
size: animated.clamp(),
|
||
keyword_info: None,
|
||
}
|
||
}
|
||
}
|
||
|
||
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
|
||
#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
|
||
/// Specifies a prioritized list of font family names or generic family names.
|
||
pub struct FontFamily(pub FontFamilyList);
|
||
|
||
impl FontFamily {
|
||
#[inline]
|
||
/// Get default font family as `serif` which is a generic font-family
|
||
pub fn serif() -> Self {
|
||
FontFamily(FontFamilyList::new(Box::new([
|
||
SingleFontFamily::Generic(atom!("serif")),
|
||
])))
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "gecko")]
|
||
impl MallocSizeOf for FontFamily {
|
||
fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {
|
||
// SharedFontList objects are generally shared from the pointer
|
||
// stored in the specified value. So only count this if the
|
||
// SharedFontList is unshared.
|
||
unsafe { bindings::Gecko_SharedFontList_SizeOfIncludingThisIfUnshared((self.0).0.get()) }
|
||
}
|
||
}
|
||
|
||
impl ToCss for FontFamily {
|
||
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
|
||
where
|
||
W: fmt::Write,
|
||
{
|
||
let mut iter = self.0.iter();
|
||
iter.next().unwrap().to_css(dest)?;
|
||
for family in iter {
|
||
dest.write_str(", ")?;
|
||
family.to_css(dest)?;
|
||
}
|
||
Ok(())
|
||
}
|
||
}
|
||
|
||
#[derive(Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq)]
|
||
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
|
||
/// The name of a font family of choice
|
||
pub struct FamilyName {
|
||
/// Name of the font family
|
||
pub name: Atom,
|
||
/// Syntax of the font family
|
||
pub syntax: FamilyNameSyntax,
|
||
}
|
||
|
||
impl ToCss for FamilyName {
|
||
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
|
||
where
|
||
W: fmt::Write,
|
||
{
|
||
match self.syntax {
|
||
FamilyNameSyntax::Quoted => {
|
||
dest.write_char('"')?;
|
||
write!(CssStringWriter::new(dest), "{}", self.name)?;
|
||
dest.write_char('"')
|
||
},
|
||
FamilyNameSyntax::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, Debug, Eq, Hash, MallocSizeOf, PartialEq)]
|
||
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
|
||
/// Font family names must either be given quoted as strings,
|
||
/// or unquoted as a sequence of one or more identifiers.
|
||
pub enum FamilyNameSyntax {
|
||
/// The family name was specified in a quoted form, e.g. "Font Name"
|
||
/// or 'Font Name'.
|
||
Quoted,
|
||
|
||
/// The family name was specified in an unquoted form as a sequence of
|
||
/// identifiers.
|
||
Identifiers,
|
||
}
|
||
|
||
#[derive(Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq)]
|
||
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
|
||
/// A set of faces that vary in weight, width or slope.
|
||
pub enum SingleFontFamily {
|
||
/// The name of a font family of choice.
|
||
FamilyName(FamilyName),
|
||
/// Generic family name.
|
||
Generic(Atom),
|
||
}
|
||
|
||
impl SingleFontFamily {
|
||
#[inline]
|
||
/// Get font family name as Atom
|
||
pub fn atom(&self) -> &Atom {
|
||
match *self {
|
||
SingleFontFamily::FamilyName(ref family_name) => &family_name.name,
|
||
SingleFontFamily::Generic(ref name) => name,
|
||
}
|
||
}
|
||
|
||
#[inline]
|
||
#[cfg(not(feature = "gecko"))] // Gecko can't borrow atoms as UTF-8.
|
||
/// Get font family name
|
||
pub fn name(&self) -> &str {
|
||
self.atom()
|
||
}
|
||
|
||
#[cfg(not(feature = "gecko"))] // Gecko can't borrow atoms as UTF-8.
|
||
/// Get the corresponding font-family with Atom
|
||
pub fn from_atom(input: Atom) -> SingleFontFamily {
|
||
match input {
|
||
atom!("serif") |
|
||
atom!("sans-serif") |
|
||
atom!("cursive") |
|
||
atom!("fantasy") |
|
||
atom!("monospace") => return SingleFontFamily::Generic(input),
|
||
_ => {},
|
||
}
|
||
match_ignore_ascii_case! { &input,
|
||
"serif" => return SingleFontFamily::Generic(atom!("serif")),
|
||
"sans-serif" => return SingleFontFamily::Generic(atom!("sans-serif")),
|
||
"cursive" => return SingleFontFamily::Generic(atom!("cursive")),
|
||
"fantasy" => return SingleFontFamily::Generic(atom!("fantasy")),
|
||
"monospace" => return SingleFontFamily::Generic(atom!("monospace")),
|
||
_ => {}
|
||
}
|
||
|
||
// We don't know if it's quoted or not. So we set it to
|
||
// quoted by default.
|
||
SingleFontFamily::FamilyName(FamilyName {
|
||
name: input,
|
||
syntax: FamilyNameSyntax::Quoted,
|
||
})
|
||
}
|
||
|
||
/// Parse a font-family value
|
||
pub fn parse<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
|
||
if let Ok(value) = input.try(|i| i.expect_string_cloned()) {
|
||
return Ok(SingleFontFamily::FamilyName(FamilyName {
|
||
name: Atom::from(&*value),
|
||
syntax: FamilyNameSyntax::Quoted,
|
||
}));
|
||
}
|
||
let first_ident = input.expect_ident()?.clone();
|
||
|
||
// FIXME(bholley): The fast thing to do here would be to look up the
|
||
// string (as lowercase) in the static atoms table. We don't have an
|
||
// API to do that yet though, so we do the simple thing for now.
|
||
let mut css_wide_keyword = false;
|
||
match_ignore_ascii_case! { &first_ident,
|
||
"serif" => return Ok(SingleFontFamily::Generic(atom!("serif"))),
|
||
"sans-serif" => return Ok(SingleFontFamily::Generic(atom!("sans-serif"))),
|
||
"cursive" => return Ok(SingleFontFamily::Generic(atom!("cursive"))),
|
||
"fantasy" => return Ok(SingleFontFamily::Generic(atom!("fantasy"))),
|
||
"monospace" => return Ok(SingleFontFamily::Generic(atom!("monospace"))),
|
||
|
||
#[cfg(feature = "gecko")]
|
||
"-moz-fixed" => return Ok(SingleFontFamily::Generic(atom!("-moz-fixed"))),
|
||
|
||
// https://drafts.csswg.org/css-fonts/#propdef-font-family
|
||
// "Font family names that happen to be the same as a keyword value
|
||
// (`inherit`, `serif`, `sans-serif`, `monospace`, `fantasy`, and `cursive`)
|
||
// must be quoted to prevent confusion with the keywords with the same names.
|
||
// The keywords ‘initial’ and ‘default’ are reserved for future use
|
||
// and must also be quoted when used as font names.
|
||
// UAs must not consider these keywords as matching the <family-name> type."
|
||
"inherit" => css_wide_keyword = true,
|
||
"initial" => css_wide_keyword = true,
|
||
"unset" => css_wide_keyword = true,
|
||
"default" => css_wide_keyword = true,
|
||
_ => {}
|
||
}
|
||
|
||
let mut value = first_ident.as_ref().to_owned();
|
||
|
||
// These keywords are not allowed by themselves.
|
||
// The only way this value can be valid with with another keyword.
|
||
if css_wide_keyword {
|
||
let ident = input.expect_ident()?;
|
||
value.push(' ');
|
||
value.push_str(&ident);
|
||
}
|
||
while let Ok(ident) = input.try(|i| i.expect_ident_cloned()) {
|
||
value.push(' ');
|
||
value.push_str(&ident);
|
||
}
|
||
let syntax = if value.starts_with(' ') || value.ends_with(' ') || value.contains(" ") {
|
||
// For font family names which contains special white spaces, e.g.
|
||
// `font-family: \ a\ \ b\ \ c\ ;`, it is tricky to serialize them
|
||
// as identifiers correctly. Just mark them quoted so we don't need
|
||
// to worry about them in serialization code.
|
||
FamilyNameSyntax::Quoted
|
||
} else {
|
||
FamilyNameSyntax::Identifiers
|
||
};
|
||
Ok(SingleFontFamily::FamilyName(FamilyName {
|
||
name: Atom::from(value),
|
||
syntax,
|
||
}))
|
||
}
|
||
|
||
#[cfg(feature = "gecko")]
|
||
/// Return the generic ID for a given generic font name
|
||
pub fn generic(name: &Atom) -> (structs::FontFamilyType, u8) {
|
||
use gecko_bindings::structs::FontFamilyType;
|
||
if *name == atom!("serif") {
|
||
(FontFamilyType::eFamily_serif, structs::kGenericFont_serif)
|
||
} else if *name == atom!("sans-serif") {
|
||
(
|
||
FontFamilyType::eFamily_sans_serif,
|
||
structs::kGenericFont_sans_serif,
|
||
)
|
||
} else if *name == atom!("cursive") {
|
||
(
|
||
FontFamilyType::eFamily_cursive,
|
||
structs::kGenericFont_cursive,
|
||
)
|
||
} else if *name == atom!("fantasy") {
|
||
(
|
||
FontFamilyType::eFamily_fantasy,
|
||
structs::kGenericFont_fantasy,
|
||
)
|
||
} else if *name == atom!("monospace") {
|
||
(
|
||
FontFamilyType::eFamily_monospace,
|
||
structs::kGenericFont_monospace,
|
||
)
|
||
} else if *name == atom!("-moz-fixed") {
|
||
(
|
||
FontFamilyType::eFamily_moz_fixed,
|
||
structs::kGenericFont_moz_fixed,
|
||
)
|
||
} else {
|
||
panic!("Unknown generic {}", name);
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "gecko")]
|
||
/// Get the corresponding font-family with family name
|
||
fn from_font_family_name(family: &structs::FontFamilyName) -> SingleFontFamily {
|
||
use gecko_bindings::structs::FontFamilyType;
|
||
|
||
match family.mType {
|
||
FontFamilyType::eFamily_sans_serif => SingleFontFamily::Generic(atom!("sans-serif")),
|
||
FontFamilyType::eFamily_serif => SingleFontFamily::Generic(atom!("serif")),
|
||
FontFamilyType::eFamily_monospace => SingleFontFamily::Generic(atom!("monospace")),
|
||
FontFamilyType::eFamily_cursive => SingleFontFamily::Generic(atom!("cursive")),
|
||
FontFamilyType::eFamily_fantasy => SingleFontFamily::Generic(atom!("fantasy")),
|
||
FontFamilyType::eFamily_moz_fixed => {
|
||
SingleFontFamily::Generic(atom!("-moz-fixed"))
|
||
},
|
||
FontFamilyType::eFamily_named => {
|
||
let name = Atom::from(&*family.mName);
|
||
SingleFontFamily::FamilyName(FamilyName {
|
||
name,
|
||
syntax: FamilyNameSyntax::Identifiers,
|
||
})
|
||
},
|
||
FontFamilyType::eFamily_named_quoted => SingleFontFamily::FamilyName(FamilyName {
|
||
name: (&*family.mName).into(),
|
||
syntax: FamilyNameSyntax::Quoted,
|
||
}),
|
||
_ => panic!("Found unexpected font FontFamilyType"),
|
||
}
|
||
}
|
||
}
|
||
|
||
impl ToCss for SingleFontFamily {
|
||
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
|
||
where
|
||
W: fmt::Write,
|
||
{
|
||
match *self {
|
||
SingleFontFamily::FamilyName(ref name) => name.to_css(dest),
|
||
|
||
// All generic values accepted by the parser are known to not require escaping.
|
||
SingleFontFamily::Generic(ref name) => {
|
||
#[cfg(feature = "gecko")]
|
||
{
|
||
// We should treat -moz-fixed as monospace
|
||
if name == &atom!("-moz-fixed") {
|
||
return dest.write_str("monospace");
|
||
}
|
||
}
|
||
|
||
write!(dest, "{}", name)
|
||
},
|
||
}
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "servo")]
|
||
#[derive(Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq)]
|
||
/// A list of SingleFontFamily
|
||
pub struct FontFamilyList(Box<[SingleFontFamily]>);
|
||
|
||
#[cfg(feature = "gecko")]
|
||
#[derive(Clone, Debug)]
|
||
/// A list of SingleFontFamily
|
||
pub struct FontFamilyList(pub RefPtr<structs::SharedFontList>);
|
||
|
||
#[cfg(feature = "gecko")]
|
||
impl Hash for FontFamilyList {
|
||
fn hash<H>(&self, state: &mut H)
|
||
where
|
||
H: Hasher,
|
||
{
|
||
for name in self.0.mNames.iter() {
|
||
name.mType.hash(state);
|
||
name.mName.hash(state);
|
||
}
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "gecko")]
|
||
impl PartialEq for FontFamilyList {
|
||
fn eq(&self, other: &FontFamilyList) -> bool {
|
||
if self.0.mNames.len() != other.0.mNames.len() {
|
||
return false;
|
||
}
|
||
for (a, b) in self.0.mNames.iter().zip(other.0.mNames.iter()) {
|
||
if a.mType != b.mType || &*a.mName != &*b.mName {
|
||
return false;
|
||
}
|
||
}
|
||
true
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "gecko")]
|
||
impl Eq for FontFamilyList {}
|
||
|
||
impl FontFamilyList {
|
||
#[cfg(feature = "servo")]
|
||
/// Return FontFamilyList with a vector of SingleFontFamily
|
||
pub fn new(families: Box<[SingleFontFamily]>) -> FontFamilyList {
|
||
FontFamilyList(families)
|
||
}
|
||
|
||
#[cfg(feature = "gecko")]
|
||
/// Return FontFamilyList with a vector of SingleFontFamily
|
||
pub fn new(families: Box<[SingleFontFamily]>) -> FontFamilyList {
|
||
let fontlist;
|
||
let names;
|
||
unsafe {
|
||
fontlist = bindings::Gecko_SharedFontList_Create();
|
||
names = &mut (*fontlist).mNames;
|
||
names.ensure_capacity(families.len());
|
||
};
|
||
|
||
for family in families.iter() {
|
||
match *family {
|
||
SingleFontFamily::FamilyName(ref f) => {
|
||
let quoted = matches!(f.syntax, FamilyNameSyntax::Quoted);
|
||
unsafe {
|
||
bindings::Gecko_nsTArray_FontFamilyName_AppendNamed(
|
||
names,
|
||
f.name.as_ptr(),
|
||
quoted,
|
||
);
|
||
}
|
||
},
|
||
SingleFontFamily::Generic(ref name) => {
|
||
let (family_type, _generic) = SingleFontFamily::generic(name);
|
||
unsafe {
|
||
bindings::Gecko_nsTArray_FontFamilyName_AppendGeneric(names, family_type);
|
||
}
|
||
},
|
||
}
|
||
}
|
||
|
||
FontFamilyList(unsafe { RefPtr::from_addrefed(fontlist) })
|
||
}
|
||
|
||
#[cfg(feature = "servo")]
|
||
/// Return iterator of SingleFontFamily
|
||
pub fn iter(&self) -> slice::Iter<SingleFontFamily> {
|
||
self.0.iter()
|
||
}
|
||
|
||
#[cfg(feature = "gecko")]
|
||
/// Return iterator of SingleFontFamily
|
||
pub fn iter(&self) -> FontFamilyNameIter {
|
||
FontFamilyNameIter {
|
||
names: &self.0.mNames,
|
||
cur: 0,
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "gecko")]
|
||
/// Return the generic ID if it is a single generic font
|
||
pub fn single_generic(&self) -> Option<u8> {
|
||
let mut iter = self.iter();
|
||
if let Some(SingleFontFamily::Generic(ref name)) = iter.next() {
|
||
if iter.next().is_none() {
|
||
return Some(SingleFontFamily::generic(name).1);
|
||
}
|
||
}
|
||
None
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "gecko")]
|
||
/// Iterator of FontFamily
|
||
pub struct FontFamilyNameIter<'a> {
|
||
names: &'a structs::nsTArray<structs::FontFamilyName>,
|
||
cur: usize,
|
||
}
|
||
|
||
#[cfg(feature = "gecko")]
|
||
impl<'a> Iterator for FontFamilyNameIter<'a> {
|
||
type Item = SingleFontFamily;
|
||
|
||
fn next(&mut self) -> Option<Self::Item> {
|
||
if self.cur < self.names.len() {
|
||
let item = SingleFontFamily::from_font_family_name(&self.names[self.cur]);
|
||
self.cur += 1;
|
||
Some(item)
|
||
} else {
|
||
None
|
||
}
|
||
}
|
||
}
|
||
|
||
#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf, PartialEq, ToCss)]
|
||
/// Preserve the readability of text when font fallback occurs
|
||
pub enum FontSizeAdjust {
|
||
#[animation(error)]
|
||
/// None variant
|
||
None,
|
||
/// Number variant
|
||
Number(CSSFloat),
|
||
}
|
||
|
||
impl FontSizeAdjust {
|
||
#[inline]
|
||
/// Default value of font-size-adjust
|
||
pub fn none() -> Self {
|
||
FontSizeAdjust::None
|
||
}
|
||
|
||
/// Get font-size-adjust with float number
|
||
pub fn from_gecko_adjust(gecko: f32) -> Self {
|
||
if gecko == -1.0 {
|
||
FontSizeAdjust::None
|
||
} else {
|
||
FontSizeAdjust::Number(gecko)
|
||
}
|
||
}
|
||
}
|
||
|
||
impl ToAnimatedZero for FontSizeAdjust {
|
||
#[inline]
|
||
// FIXME(emilio): why?
|
||
fn to_animated_zero(&self) -> Result<Self, ()> {
|
||
Err(())
|
||
}
|
||
}
|
||
|
||
impl ToAnimatedValue for FontSizeAdjust {
|
||
type AnimatedValue = Self;
|
||
|
||
#[inline]
|
||
fn to_animated_value(self) -> Self {
|
||
self
|
||
}
|
||
|
||
#[inline]
|
||
fn from_animated_value(animated: Self::AnimatedValue) -> Self {
|
||
match animated {
|
||
FontSizeAdjust::Number(number) => FontSizeAdjust::Number(number.max(0.)),
|
||
_ => animated,
|
||
}
|
||
}
|
||
}
|
||
|
||
/// Use VariantAlternatesList as computed type of FontVariantAlternates
|
||
pub type FontVariantAlternates = specified::VariantAlternatesList;
|
||
|
||
impl FontVariantAlternates {
|
||
#[inline]
|
||
/// Get initial value with VariantAlternatesList
|
||
pub fn get_initial_value() -> Self {
|
||
specified::VariantAlternatesList(vec![].into_boxed_slice())
|
||
}
|
||
}
|
||
|
||
/// Use VariantEastAsian as computed type of FontVariantEastAsian
|
||
pub type FontVariantEastAsian = specified::VariantEastAsian;
|
||
|
||
/// Use VariantLigatures as computed type of FontVariantLigatures
|
||
pub type FontVariantLigatures = specified::VariantLigatures;
|
||
|
||
/// Use VariantNumeric as computed type of FontVariantNumeric
|
||
pub type FontVariantNumeric = specified::VariantNumeric;
|
||
|
||
/// Use FontSettings as computed type of FontFeatureSettings.
|
||
pub type FontFeatureSettings = FontSettings<FeatureTagValue<Integer>>;
|
||
|
||
/// The computed value for font-variation-settings.
|
||
pub type FontVariationSettings = FontSettings<VariationValue<Number>>;
|
||
|
||
/// font-language-override can only have a single three-letter
|
||
/// OpenType "language system" tag, so we should be able to compute
|
||
/// it and store it as a 32-bit integer
|
||
/// (see http://www.microsoft.com/typography/otspec/languagetags.htm).
|
||
#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq)]
|
||
pub struct FontLanguageOverride(pub u32);
|
||
|
||
impl FontLanguageOverride {
|
||
#[inline]
|
||
/// Get computed default value of `font-language-override` with 0
|
||
pub fn zero() -> FontLanguageOverride {
|
||
FontLanguageOverride(0)
|
||
}
|
||
}
|
||
|
||
impl ToCss for FontLanguageOverride {
|
||
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
|
||
where
|
||
W: fmt::Write,
|
||
{
|
||
use std::str;
|
||
|
||
if self.0 == 0 {
|
||
return dest.write_str("normal");
|
||
}
|
||
let mut buf = [0; 4];
|
||
BigEndian::write_u32(&mut buf, self.0);
|
||
// Safe because we ensure it's ASCII during computing
|
||
let slice = if cfg!(debug_assertions) {
|
||
str::from_utf8(&buf).unwrap()
|
||
} else {
|
||
unsafe { str::from_utf8_unchecked(&buf) }
|
||
};
|
||
slice.trim_right().to_css(dest)
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "gecko")]
|
||
impl From<u32> for FontLanguageOverride {
|
||
fn from(bits: u32) -> FontLanguageOverride {
|
||
FontLanguageOverride(bits)
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "gecko")]
|
||
impl From<FontLanguageOverride> for u32 {
|
||
fn from(v: FontLanguageOverride) -> u32 {
|
||
v.0
|
||
}
|
||
}
|
||
|
||
impl ToComputedValue for specified::MozScriptMinSize {
|
||
type ComputedValue = MozScriptMinSize;
|
||
|
||
fn to_computed_value(&self, cx: &Context) -> MozScriptMinSize {
|
||
// this value is used in the computation of font-size, so
|
||
// we use the parent size
|
||
let base_size = FontBaseSize::InheritedStyle;
|
||
match self.0 {
|
||
NoCalcLength::FontRelative(value) => value.to_computed_value(cx, base_size),
|
||
NoCalcLength::ServoCharacterWidth(value) => {
|
||
value.to_computed_value(base_size.resolve(cx))
|
||
},
|
||
ref l => l.to_computed_value(cx),
|
||
}
|
||
}
|
||
|
||
fn from_computed_value(other: &MozScriptMinSize) -> Self {
|
||
specified::MozScriptMinSize(ToComputedValue::from_computed_value(other))
|
||
}
|
||
}
|
||
|
||
/// The computed value of the -moz-script-level property.
|
||
pub type MozScriptLevel = i8;
|
||
|
||
#[cfg(feature = "gecko")]
|
||
impl ToComputedValue for specified::MozScriptLevel {
|
||
type ComputedValue = MozScriptLevel;
|
||
|
||
fn to_computed_value(&self, cx: &Context) -> i8 {
|
||
use properties::longhands::_moz_math_display::SpecifiedValue as DisplayValue;
|
||
use std::{cmp, i8};
|
||
|
||
let int = match *self {
|
||
specified::MozScriptLevel::Auto => {
|
||
let parent = cx.builder.get_parent_font().clone__moz_script_level() as i32;
|
||
let display = cx.builder.get_parent_font().clone__moz_math_display();
|
||
if display == DisplayValue::Inline {
|
||
parent + 1
|
||
} else {
|
||
parent
|
||
}
|
||
},
|
||
specified::MozScriptLevel::Relative(rel) => {
|
||
let parent = cx.builder.get_parent_font().clone__moz_script_level();
|
||
parent as i32 + rel
|
||
},
|
||
specified::MozScriptLevel::MozAbsolute(abs) => abs,
|
||
};
|
||
cmp::min(int, i8::MAX as i32) as i8
|
||
}
|
||
|
||
fn from_computed_value(other: &i8) -> Self {
|
||
specified::MozScriptLevel::MozAbsolute(*other as i32)
|
||
}
|
||
}
|
||
|
||
/// A wrapper over an `Angle`, that handles clamping to the appropriate range
|
||
/// for `font-style` animation.
|
||
#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToCss)]
|
||
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
|
||
pub struct FontStyleAngle(pub Angle);
|
||
|
||
impl ToAnimatedValue for FontStyleAngle {
|
||
type AnimatedValue = Angle;
|
||
|
||
#[inline]
|
||
fn to_animated_value(self) -> Self::AnimatedValue {
|
||
self.0
|
||
}
|
||
|
||
#[inline]
|
||
fn from_animated_value(animated: Self::AnimatedValue) -> Self {
|
||
FontStyleAngle(Angle::Deg(
|
||
animated.degrees()
|
||
.min(specified::FONT_STYLE_OBLIQUE_MAX_ANGLE_DEGREES)
|
||
.max(specified::FONT_STYLE_OBLIQUE_MIN_ANGLE_DEGREES)
|
||
))
|
||
}
|
||
}
|
||
|
||
impl Hash for FontStyleAngle {
|
||
fn hash<H: Hasher>(&self, hasher: &mut H) {
|
||
hasher.write_u64((self.0.degrees() * 10000.).trunc() as u64);
|
||
}
|
||
}
|
||
|
||
/// The computed value of `font-style`.
|
||
///
|
||
/// FIXME(emilio): Angle should be a custom type to handle clamping during
|
||
/// animation.
|
||
pub type FontStyle = generics::FontStyle<FontStyleAngle>;
|
||
|
||
impl FontStyle {
|
||
/// The `normal` value.
|
||
#[inline]
|
||
pub fn normal() -> Self {
|
||
generics::FontStyle::Normal
|
||
}
|
||
|
||
/// The default angle for font-style: oblique. This is 20deg per spec:
|
||
///
|
||
/// https://drafts.csswg.org/css-fonts-4/#valdef-font-style-oblique-angle
|
||
#[inline]
|
||
pub fn default_angle() -> FontStyleAngle {
|
||
FontStyleAngle(Angle::Deg(specified::DEFAULT_FONT_STYLE_OBLIQUE_ANGLE_DEGREES))
|
||
}
|
||
|
||
|
||
/// Get the font style from Gecko's nsFont struct.
|
||
#[cfg(feature = "gecko")]
|
||
pub fn from_gecko(style: structs::FontSlantStyle) -> Self {
|
||
let mut angle = 0.;
|
||
let mut italic = false;
|
||
let mut normal = false;
|
||
unsafe {
|
||
bindings::Gecko_FontSlantStyle_Get(style, &mut normal, &mut italic, &mut angle);
|
||
}
|
||
if normal {
|
||
return generics::FontStyle::Normal;
|
||
}
|
||
if italic {
|
||
return generics::FontStyle::Italic;
|
||
}
|
||
generics::FontStyle::Oblique(FontStyleAngle(Angle::Deg(angle)))
|
||
}
|
||
}
|
||
|
||
impl ToCss for FontStyle {
|
||
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
|
||
where
|
||
W: fmt::Write,
|
||
{
|
||
match *self {
|
||
generics::FontStyle::Normal => dest.write_str("normal"),
|
||
generics::FontStyle::Italic => dest.write_str("italic"),
|
||
generics::FontStyle::Oblique(ref angle) => {
|
||
dest.write_str("oblique")?;
|
||
// Use `degrees` instead of just comparing Angle because
|
||
// `degrees` can return slightly different values due to
|
||
// floating point conversions.
|
||
if angle.0.degrees() != Self::default_angle().0.degrees() {
|
||
dest.write_char(' ')?;
|
||
angle.to_css(dest)?;
|
||
}
|
||
Ok(())
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/// A value for the font-stretch property per:
|
||
///
|
||
/// https://drafts.csswg.org/css-fonts-4/#propdef-font-stretch
|
||
#[derive(Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf, PartialEq, ToCss)]
|
||
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
|
||
pub struct FontStretch(pub NonNegativePercentage);
|
||
|
||
impl FontStretch {
|
||
/// 100%
|
||
pub fn hundred() -> Self {
|
||
FontStretch(NonNegativePercentage::hundred())
|
||
}
|
||
|
||
/// The float value of the percentage
|
||
#[inline]
|
||
pub fn value(&self) -> CSSFloat {
|
||
((self.0).0).0
|
||
}
|
||
}
|
||
|
||
impl ToAnimatedValue for FontStretch {
|
||
type AnimatedValue = Percentage;
|
||
|
||
#[inline]
|
||
fn to_animated_value(self) -> Self::AnimatedValue {
|
||
self.0.to_animated_value()
|
||
}
|
||
|
||
#[inline]
|
||
fn from_animated_value(animated: Self::AnimatedValue) -> Self {
|
||
FontStretch(NonNegativePercentage::from_animated_value(animated))
|
||
}
|
||
}
|
||
|
||
impl Hash for FontStretch {
|
||
fn hash<H: Hasher>(&self, hasher: &mut H) {
|
||
hasher.write_u64((self.value() * 10000.).trunc() as u64);
|
||
}
|
||
}
|