1969 lines
63 KiB
Rust
1969 lines
63 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 https://mozilla.org/MPL/2.0/. */
|
|
|
|
//! Specified values for font properties
|
|
|
|
use crate::context::QuirksMode;
|
|
use crate::parser::{Parse, ParserContext};
|
|
use crate::values::computed::font::{FamilyName, FontFamilyList, SingleFontFamily};
|
|
use crate::values::computed::Percentage as ComputedPercentage;
|
|
use crate::values::computed::{font as computed, Length, NonNegativeLength};
|
|
use crate::values::computed::{CSSPixelLength, Context, ToComputedValue};
|
|
use crate::values::generics::font::{
|
|
self as generics, FeatureTagValue, FontSettings, FontTag, GenericLineHeight, VariationValue,
|
|
};
|
|
use crate::values::generics::NonNegative;
|
|
use crate::values::specified::length::{FontBaseSize, LineHeightBase, PX_PER_PT};
|
|
use crate::values::specified::{AllowQuirks, Angle, Integer, LengthPercentage};
|
|
use crate::values::specified::{
|
|
FontRelativeLength, NoCalcLength, NonNegativeLengthPercentage, NonNegativeNumber,
|
|
NonNegativePercentage, Number,
|
|
};
|
|
use crate::values::{serialize_atom_identifier, CustomIdent, SelectorParseErrorKind};
|
|
use crate::Atom;
|
|
use cssparser::{Parser, Token};
|
|
#[cfg(feature = "gecko")]
|
|
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps, MallocUnconditionalSizeOf};
|
|
use std::fmt::{self, Write};
|
|
use style_traits::{CssWriter, KeywordsCollectFn, ParseError};
|
|
use style_traits::{SpecifiedValueInfo, StyleParseErrorKind, ToCss};
|
|
|
|
// FIXME(emilio): The system font code is copy-pasta, and should be cleaned up.
|
|
macro_rules! system_font_methods {
|
|
($ty:ident, $field:ident) => {
|
|
system_font_methods!($ty);
|
|
|
|
fn compute_system(&self, _context: &Context) -> <$ty as ToComputedValue>::ComputedValue {
|
|
debug_assert!(matches!(*self, $ty::System(..)));
|
|
#[cfg(feature = "gecko")]
|
|
{
|
|
_context.cached_system_font.as_ref().unwrap().$field.clone()
|
|
}
|
|
#[cfg(feature = "servo")]
|
|
{
|
|
unreachable!()
|
|
}
|
|
}
|
|
};
|
|
|
|
($ty:ident) => {
|
|
/// Get a specified value that represents a system font.
|
|
pub fn system_font(f: SystemFont) -> Self {
|
|
$ty::System(f)
|
|
}
|
|
|
|
/// Retreive a SystemFont from the specified value.
|
|
pub fn get_system(&self) -> Option<SystemFont> {
|
|
if let $ty::System(s) = *self {
|
|
Some(s)
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
/// System fonts.
|
|
#[repr(u8)]
|
|
#[derive(
|
|
Clone, Copy, Debug, Eq, Hash, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem,
|
|
)]
|
|
#[allow(missing_docs)]
|
|
#[cfg(feature = "gecko")]
|
|
pub enum SystemFont {
|
|
/// https://drafts.csswg.org/css-fonts/#valdef-font-caption
|
|
Caption,
|
|
/// https://drafts.csswg.org/css-fonts/#valdef-font-icon
|
|
Icon,
|
|
/// https://drafts.csswg.org/css-fonts/#valdef-font-menu
|
|
Menu,
|
|
/// https://drafts.csswg.org/css-fonts/#valdef-font-message-box
|
|
MessageBox,
|
|
/// https://drafts.csswg.org/css-fonts/#valdef-font-small-caption
|
|
SmallCaption,
|
|
/// https://drafts.csswg.org/css-fonts/#valdef-font-status-bar
|
|
StatusBar,
|
|
/// Internal system font, used by the `<menupopup>`s on macOS.
|
|
#[parse(condition = "ParserContext::chrome_rules_enabled")]
|
|
MozPullDownMenu,
|
|
/// Internal system font, used for `<button>` elements.
|
|
#[parse(condition = "ParserContext::chrome_rules_enabled")]
|
|
MozButton,
|
|
/// Internal font, used by `<select>` elements.
|
|
#[parse(condition = "ParserContext::chrome_rules_enabled")]
|
|
MozList,
|
|
/// Internal font, used by `<input>` elements.
|
|
#[parse(condition = "ParserContext::chrome_rules_enabled")]
|
|
MozField,
|
|
#[css(skip)]
|
|
End, // Just for indexing purposes.
|
|
}
|
|
|
|
// We don't parse system fonts in servo, but in the interest of not
|
|
// littering a lot of code with `if engine == "gecko"` conditionals,
|
|
// we have a dummy system font module that does nothing
|
|
|
|
#[derive(
|
|
Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem
|
|
)]
|
|
#[allow(missing_docs)]
|
|
#[cfg(feature = "servo")]
|
|
/// void enum for system font, can never exist
|
|
pub enum SystemFont {}
|
|
|
|
#[allow(missing_docs)]
|
|
#[cfg(feature = "servo")]
|
|
impl SystemFont {
|
|
pub fn parse(_: &mut Parser) -> Result<Self, ()> {
|
|
Err(())
|
|
}
|
|
}
|
|
|
|
const DEFAULT_SCRIPT_MIN_SIZE_PT: u32 = 8;
|
|
const DEFAULT_SCRIPT_SIZE_MULTIPLIER: f64 = 0.71;
|
|
|
|
/// The minimum font-weight value per:
|
|
///
|
|
/// https://drafts.csswg.org/css-fonts-4/#font-weight-numeric-values
|
|
pub const MIN_FONT_WEIGHT: f32 = 1.;
|
|
|
|
/// The maximum font-weight value per:
|
|
///
|
|
/// https://drafts.csswg.org/css-fonts-4/#font-weight-numeric-values
|
|
pub const MAX_FONT_WEIGHT: f32 = 1000.;
|
|
|
|
/// A specified font-weight value.
|
|
///
|
|
/// https://drafts.csswg.org/css-fonts-4/#propdef-font-weight
|
|
#[derive(
|
|
Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem,
|
|
)]
|
|
pub enum FontWeight {
|
|
/// `<font-weight-absolute>`
|
|
Absolute(AbsoluteFontWeight),
|
|
/// Bolder variant
|
|
Bolder,
|
|
/// Lighter variant
|
|
Lighter,
|
|
/// System font variant.
|
|
#[css(skip)]
|
|
System(SystemFont),
|
|
}
|
|
|
|
impl FontWeight {
|
|
system_font_methods!(FontWeight, font_weight);
|
|
|
|
/// `normal`
|
|
#[inline]
|
|
pub fn normal() -> Self {
|
|
FontWeight::Absolute(AbsoluteFontWeight::Normal)
|
|
}
|
|
|
|
/// Get a specified FontWeight from a gecko keyword
|
|
pub fn from_gecko_keyword(kw: u32) -> Self {
|
|
debug_assert!(kw % 100 == 0);
|
|
debug_assert!(kw as f32 <= MAX_FONT_WEIGHT);
|
|
FontWeight::Absolute(AbsoluteFontWeight::Weight(Number::new(kw as f32)))
|
|
}
|
|
}
|
|
|
|
impl ToComputedValue for FontWeight {
|
|
type ComputedValue = computed::FontWeight;
|
|
|
|
#[inline]
|
|
fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
|
|
match *self {
|
|
FontWeight::Absolute(ref abs) => abs.compute(),
|
|
FontWeight::Bolder => context
|
|
.builder
|
|
.get_parent_font()
|
|
.clone_font_weight()
|
|
.bolder(),
|
|
FontWeight::Lighter => context
|
|
.builder
|
|
.get_parent_font()
|
|
.clone_font_weight()
|
|
.lighter(),
|
|
FontWeight::System(_) => self.compute_system(context),
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
fn from_computed_value(computed: &computed::FontWeight) -> Self {
|
|
FontWeight::Absolute(AbsoluteFontWeight::Weight(Number::from_computed_value(
|
|
&computed.value(),
|
|
)))
|
|
}
|
|
}
|
|
|
|
/// An absolute font-weight value for a @font-face rule.
|
|
///
|
|
/// https://drafts.csswg.org/css-fonts-4/#font-weight-absolute-values
|
|
#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
|
|
pub enum AbsoluteFontWeight {
|
|
/// A `<number>`, with the additional constraints specified in:
|
|
///
|
|
/// https://drafts.csswg.org/css-fonts-4/#font-weight-numeric-values
|
|
Weight(Number),
|
|
/// Normal font weight. Same as 400.
|
|
Normal,
|
|
/// Bold font weight. Same as 700.
|
|
Bold,
|
|
}
|
|
|
|
impl AbsoluteFontWeight {
|
|
/// Returns the computed value for this absolute font weight.
|
|
pub fn compute(&self) -> computed::FontWeight {
|
|
match *self {
|
|
AbsoluteFontWeight::Weight(weight) => computed::FontWeight::from_float(weight.get()),
|
|
AbsoluteFontWeight::Normal => computed::FontWeight::NORMAL,
|
|
AbsoluteFontWeight::Bold => computed::FontWeight::BOLD,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Parse for AbsoluteFontWeight {
|
|
fn parse<'i, 't>(
|
|
context: &ParserContext,
|
|
input: &mut Parser<'i, 't>,
|
|
) -> Result<Self, ParseError<'i>> {
|
|
if let Ok(number) = input.try_parse(|input| Number::parse(context, input)) {
|
|
// We could add another AllowedNumericType value, but it doesn't
|
|
// seem worth it just for a single property with such a weird range,
|
|
// so we do the clamping here manually.
|
|
if !number.was_calc() &&
|
|
(number.get() < MIN_FONT_WEIGHT || number.get() > MAX_FONT_WEIGHT)
|
|
{
|
|
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
|
|
}
|
|
return Ok(AbsoluteFontWeight::Weight(number));
|
|
}
|
|
|
|
Ok(try_match_ident_ignore_ascii_case! { input,
|
|
"normal" => AbsoluteFontWeight::Normal,
|
|
"bold" => AbsoluteFontWeight::Bold,
|
|
})
|
|
}
|
|
}
|
|
|
|
/// The specified value of the `font-style` property, without the system font
|
|
/// crap.
|
|
pub type SpecifiedFontStyle = generics::FontStyle<Angle>;
|
|
|
|
impl ToCss for SpecifiedFontStyle {
|
|
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
|
|
where
|
|
W: Write,
|
|
{
|
|
match *self {
|
|
generics::FontStyle::Italic => dest.write_str("italic"),
|
|
generics::FontStyle::Oblique(ref angle) => {
|
|
// Not angle.is_zero() because we don't want to serialize
|
|
// `oblique calc(0deg)` as `normal`.
|
|
if *angle == Angle::zero() {
|
|
dest.write_str("normal")?;
|
|
} else {
|
|
dest.write_str("oblique")?;
|
|
if *angle != Self::default_angle() {
|
|
dest.write_char(' ')?;
|
|
angle.to_css(dest)?;
|
|
}
|
|
}
|
|
Ok(())
|
|
},
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Parse for SpecifiedFontStyle {
|
|
fn parse<'i, 't>(
|
|
context: &ParserContext,
|
|
input: &mut Parser<'i, 't>,
|
|
) -> Result<Self, ParseError<'i>> {
|
|
Ok(try_match_ident_ignore_ascii_case! { input,
|
|
"normal" => generics::FontStyle::normal(),
|
|
"italic" => generics::FontStyle::Italic,
|
|
"oblique" => {
|
|
let angle = input.try_parse(|input| Self::parse_angle(context, input))
|
|
.unwrap_or_else(|_| Self::default_angle());
|
|
|
|
generics::FontStyle::Oblique(angle)
|
|
},
|
|
})
|
|
}
|
|
}
|
|
|
|
impl ToComputedValue for SpecifiedFontStyle {
|
|
type ComputedValue = computed::FontStyle;
|
|
|
|
fn to_computed_value(&self, _: &Context) -> Self::ComputedValue {
|
|
match *self {
|
|
Self::Italic => computed::FontStyle::ITALIC,
|
|
Self::Oblique(ref angle) => computed::FontStyle::oblique(angle.degrees()),
|
|
}
|
|
}
|
|
|
|
fn from_computed_value(computed: &Self::ComputedValue) -> Self {
|
|
if *computed == computed::FontStyle::ITALIC {
|
|
return Self::Italic;
|
|
}
|
|
let degrees = computed.oblique_degrees();
|
|
generics::FontStyle::Oblique(Angle::from_degrees(degrees, /* was_calc = */ false))
|
|
}
|
|
}
|
|
|
|
/// From https://drafts.csswg.org/css-fonts-4/#valdef-font-style-oblique-angle:
|
|
///
|
|
/// Values less than -90deg or values greater than 90deg are
|
|
/// invalid and are treated as parse errors.
|
|
///
|
|
/// The maximum angle value that `font-style: oblique` should compute to.
|
|
pub const FONT_STYLE_OBLIQUE_MAX_ANGLE_DEGREES: f32 = 90.;
|
|
|
|
/// The minimum angle value that `font-style: oblique` should compute to.
|
|
pub const FONT_STYLE_OBLIQUE_MIN_ANGLE_DEGREES: f32 = -90.;
|
|
|
|
impl SpecifiedFontStyle {
|
|
/// Gets a clamped angle in degrees from a specified Angle.
|
|
pub fn compute_angle_degrees(angle: &Angle) -> f32 {
|
|
angle
|
|
.degrees()
|
|
.max(FONT_STYLE_OBLIQUE_MIN_ANGLE_DEGREES)
|
|
.min(FONT_STYLE_OBLIQUE_MAX_ANGLE_DEGREES)
|
|
}
|
|
|
|
/// Parse a suitable angle for font-style: oblique.
|
|
pub fn parse_angle<'i, 't>(
|
|
context: &ParserContext,
|
|
input: &mut Parser<'i, 't>,
|
|
) -> Result<Angle, ParseError<'i>> {
|
|
let angle = Angle::parse(context, input)?;
|
|
if angle.was_calc() {
|
|
return Ok(angle);
|
|
}
|
|
|
|
let degrees = angle.degrees();
|
|
if degrees < FONT_STYLE_OBLIQUE_MIN_ANGLE_DEGREES ||
|
|
degrees > FONT_STYLE_OBLIQUE_MAX_ANGLE_DEGREES
|
|
{
|
|
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
|
|
}
|
|
return Ok(angle);
|
|
}
|
|
|
|
/// The default angle for `font-style: oblique`.
|
|
pub fn default_angle() -> Angle {
|
|
Angle::from_degrees(
|
|
computed::FontStyle::DEFAULT_OBLIQUE_DEGREES as f32,
|
|
/* was_calc = */ false,
|
|
)
|
|
}
|
|
}
|
|
|
|
/// The specified value of the `font-style` property.
|
|
#[derive(
|
|
Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem,
|
|
)]
|
|
#[allow(missing_docs)]
|
|
pub enum FontStyle {
|
|
Specified(SpecifiedFontStyle),
|
|
#[css(skip)]
|
|
System(SystemFont),
|
|
}
|
|
|
|
impl FontStyle {
|
|
/// Return the `normal` value.
|
|
#[inline]
|
|
pub fn normal() -> Self {
|
|
FontStyle::Specified(generics::FontStyle::normal())
|
|
}
|
|
|
|
system_font_methods!(FontStyle, font_style);
|
|
}
|
|
|
|
impl ToComputedValue for FontStyle {
|
|
type ComputedValue = computed::FontStyle;
|
|
|
|
fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
|
|
match *self {
|
|
FontStyle::Specified(ref specified) => specified.to_computed_value(context),
|
|
FontStyle::System(..) => self.compute_system(context),
|
|
}
|
|
}
|
|
|
|
fn from_computed_value(computed: &Self::ComputedValue) -> Self {
|
|
FontStyle::Specified(SpecifiedFontStyle::from_computed_value(computed))
|
|
}
|
|
}
|
|
|
|
/// A value for the `font-stretch` property.
|
|
///
|
|
/// https://drafts.csswg.org/css-fonts-4/#font-stretch-prop
|
|
#[allow(missing_docs)]
|
|
#[derive(
|
|
Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem,
|
|
)]
|
|
pub enum FontStretch {
|
|
Stretch(NonNegativePercentage),
|
|
Keyword(FontStretchKeyword),
|
|
#[css(skip)]
|
|
System(SystemFont),
|
|
}
|
|
|
|
/// A keyword value for `font-stretch`.
|
|
#[derive(
|
|
Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem,
|
|
)]
|
|
#[allow(missing_docs)]
|
|
pub enum FontStretchKeyword {
|
|
Normal,
|
|
Condensed,
|
|
UltraCondensed,
|
|
ExtraCondensed,
|
|
SemiCondensed,
|
|
SemiExpanded,
|
|
Expanded,
|
|
ExtraExpanded,
|
|
UltraExpanded,
|
|
}
|
|
|
|
impl FontStretchKeyword {
|
|
/// Turns the keyword into a computed value.
|
|
pub fn compute(&self) -> computed::FontStretch {
|
|
computed::FontStretch::from_keyword(*self)
|
|
}
|
|
|
|
/// Does the opposite operation to `compute`, in order to serialize keywords
|
|
/// if possible.
|
|
pub fn from_percentage(p: f32) -> Option<Self> {
|
|
computed::FontStretch::from_percentage(p).as_keyword()
|
|
}
|
|
}
|
|
|
|
impl FontStretch {
|
|
/// `normal`.
|
|
pub fn normal() -> Self {
|
|
FontStretch::Keyword(FontStretchKeyword::Normal)
|
|
}
|
|
|
|
system_font_methods!(FontStretch, font_stretch);
|
|
}
|
|
|
|
impl ToComputedValue for FontStretch {
|
|
type ComputedValue = computed::FontStretch;
|
|
|
|
fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
|
|
match *self {
|
|
FontStretch::Stretch(ref percentage) => {
|
|
let percentage = percentage.to_computed_value(context).0;
|
|
computed::FontStretch::from_percentage(percentage.0)
|
|
},
|
|
FontStretch::Keyword(ref kw) => kw.compute(),
|
|
FontStretch::System(_) => self.compute_system(context),
|
|
}
|
|
}
|
|
|
|
fn from_computed_value(computed: &Self::ComputedValue) -> Self {
|
|
FontStretch::Stretch(NonNegativePercentage::from_computed_value(&NonNegative(
|
|
computed.to_percentage(),
|
|
)))
|
|
}
|
|
}
|
|
|
|
/// CSS font keywords
|
|
#[derive(
|
|
Animate,
|
|
Clone,
|
|
ComputeSquaredDistance,
|
|
Copy,
|
|
Debug,
|
|
MallocSizeOf,
|
|
Parse,
|
|
PartialEq,
|
|
SpecifiedValueInfo,
|
|
ToAnimatedValue,
|
|
ToAnimatedZero,
|
|
ToComputedValue,
|
|
ToCss,
|
|
ToResolvedValue,
|
|
ToShmem,
|
|
Serialize,
|
|
Deserialize,
|
|
)]
|
|
#[allow(missing_docs)]
|
|
#[repr(u8)]
|
|
pub enum FontSizeKeyword {
|
|
#[css(keyword = "xx-small")]
|
|
XXSmall,
|
|
XSmall,
|
|
Small,
|
|
Medium,
|
|
Large,
|
|
XLarge,
|
|
#[css(keyword = "xx-large")]
|
|
XXLarge,
|
|
#[css(keyword = "xxx-large")]
|
|
XXXLarge,
|
|
/// Indicate whether to apply font-size: math is specified so that extra
|
|
/// scaling due to math-depth changes is applied during the cascade.
|
|
#[cfg(feature = "gecko")]
|
|
Math,
|
|
#[css(skip)]
|
|
None,
|
|
}
|
|
|
|
impl FontSizeKeyword {
|
|
/// Convert to an HTML <font size> value
|
|
#[inline]
|
|
pub fn html_size(self) -> u8 {
|
|
self as u8
|
|
}
|
|
|
|
/// Returns true if the font size is the math keyword
|
|
#[cfg(feature = "gecko")]
|
|
pub fn is_math(self) -> bool {
|
|
matches!(self, Self::Math)
|
|
}
|
|
|
|
/// Returns true if the font size is the math keyword
|
|
#[cfg(feature = "servo")]
|
|
pub fn is_math(self) -> bool {
|
|
false
|
|
}
|
|
}
|
|
|
|
impl Default for FontSizeKeyword {
|
|
fn default() -> Self {
|
|
FontSizeKeyword::Medium
|
|
}
|
|
}
|
|
|
|
#[derive(
|
|
Animate,
|
|
Clone,
|
|
ComputeSquaredDistance,
|
|
Copy,
|
|
Debug,
|
|
MallocSizeOf,
|
|
PartialEq,
|
|
ToAnimatedValue,
|
|
ToAnimatedZero,
|
|
ToComputedValue,
|
|
ToCss,
|
|
ToResolvedValue,
|
|
ToShmem,
|
|
)]
|
|
#[cfg_attr(feature = "servo", derive(Serialize, Deserialize))]
|
|
/// Additional information for keyword-derived font sizes.
|
|
pub struct KeywordInfo {
|
|
/// The keyword used
|
|
pub kw: FontSizeKeyword,
|
|
/// A factor to be multiplied by the computed size of the keyword
|
|
#[css(skip)]
|
|
pub factor: f32,
|
|
/// An additional fixed offset to add to the kw * factor in the case of
|
|
/// `calc()`.
|
|
#[css(skip)]
|
|
pub offset: CSSPixelLength,
|
|
}
|
|
|
|
impl KeywordInfo {
|
|
/// KeywordInfo value for font-size: medium
|
|
pub fn medium() -> Self {
|
|
Self::new(FontSizeKeyword::Medium)
|
|
}
|
|
|
|
/// KeywordInfo value for font-size: none
|
|
pub fn none() -> Self {
|
|
Self::new(FontSizeKeyword::None)
|
|
}
|
|
|
|
fn new(kw: FontSizeKeyword) -> Self {
|
|
KeywordInfo {
|
|
kw,
|
|
factor: 1.,
|
|
offset: CSSPixelLength::new(0.),
|
|
}
|
|
}
|
|
|
|
/// Computes the final size for this font-size keyword, accounting for
|
|
/// text-zoom.
|
|
fn to_computed_value(&self, context: &Context) -> CSSPixelLength {
|
|
debug_assert_ne!(self.kw, FontSizeKeyword::None);
|
|
#[cfg(feature="gecko")]
|
|
debug_assert_ne!(self.kw, FontSizeKeyword::Math);
|
|
let base = context.maybe_zoom_text(self.kw.to_length(context).0);
|
|
let zoom_factor = context.style().effective_zoom.value();
|
|
CSSPixelLength::new(base.px() * self.factor * zoom_factor) + context.maybe_zoom_text(self.offset)
|
|
}
|
|
|
|
/// Given a parent keyword info (self), apply an additional factor/offset to
|
|
/// it.
|
|
fn compose(self, factor: f32) -> Self {
|
|
if self.kw == FontSizeKeyword::None {
|
|
return self;
|
|
}
|
|
KeywordInfo {
|
|
kw: self.kw,
|
|
factor: self.factor * factor,
|
|
offset: self.offset * factor,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl SpecifiedValueInfo for KeywordInfo {
|
|
fn collect_completion_keywords(f: KeywordsCollectFn) {
|
|
<FontSizeKeyword as SpecifiedValueInfo>::collect_completion_keywords(f);
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
|
|
/// A specified font-size value
|
|
pub enum FontSize {
|
|
/// A length; e.g. 10px.
|
|
Length(LengthPercentage),
|
|
/// A keyword value, along with a ratio and absolute offset.
|
|
/// The ratio in any specified keyword value
|
|
/// will be 1 (with offset 0), but we cascade keywordness even
|
|
/// after font-relative (percent and em) values
|
|
/// have been applied, which is where the ratio
|
|
/// comes in. The offset comes in if we cascaded a calc value,
|
|
/// where the font-relative portion (em and percentage) will
|
|
/// go into the ratio, and the remaining units all computed together
|
|
/// will go into the offset.
|
|
/// See bug 1355707.
|
|
Keyword(KeywordInfo),
|
|
/// font-size: smaller
|
|
Smaller,
|
|
/// font-size: larger
|
|
Larger,
|
|
/// Derived from a specified system font.
|
|
#[css(skip)]
|
|
System(SystemFont),
|
|
}
|
|
|
|
/// Specifies a prioritized list of font family names or generic family names.
|
|
#[derive(Clone, Debug, Eq, PartialEq, ToCss, ToShmem)]
|
|
#[cfg_attr(feature = "servo", derive(Hash))]
|
|
pub enum FontFamily {
|
|
/// List of `font-family`
|
|
#[css(comma)]
|
|
Values(#[css(iterable)] FontFamilyList),
|
|
/// System font
|
|
#[css(skip)]
|
|
System(SystemFont),
|
|
}
|
|
|
|
impl FontFamily {
|
|
system_font_methods!(FontFamily, font_family);
|
|
}
|
|
|
|
impl ToComputedValue for FontFamily {
|
|
type ComputedValue = computed::FontFamily;
|
|
|
|
fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
|
|
match *self {
|
|
FontFamily::Values(ref list) => computed::FontFamily {
|
|
families: list.clone(),
|
|
is_system_font: false,
|
|
is_initial: false,
|
|
},
|
|
FontFamily::System(_) => self.compute_system(context),
|
|
}
|
|
}
|
|
|
|
fn from_computed_value(other: &computed::FontFamily) -> Self {
|
|
FontFamily::Values(other.families.clone())
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "gecko")]
|
|
impl MallocSizeOf for FontFamily {
|
|
fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
|
|
match *self {
|
|
FontFamily::Values(ref v) => {
|
|
// Although the family list is refcounted, we always attribute
|
|
// its size to the specified value.
|
|
v.list.unconditional_size_of(ops)
|
|
},
|
|
FontFamily::System(_) => 0,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Parse for FontFamily {
|
|
/// <family-name>#
|
|
/// <family-name> = <string> | [ <ident>+ ]
|
|
/// TODO: <generic-family>
|
|
fn parse<'i, 't>(
|
|
context: &ParserContext,
|
|
input: &mut Parser<'i, 't>,
|
|
) -> Result<FontFamily, ParseError<'i>> {
|
|
let values =
|
|
input.parse_comma_separated(|input| SingleFontFamily::parse(context, input))?;
|
|
Ok(FontFamily::Values(FontFamilyList {
|
|
list: crate::ArcSlice::from_iter(values.into_iter()),
|
|
}))
|
|
}
|
|
}
|
|
|
|
impl SpecifiedValueInfo for FontFamily {}
|
|
|
|
/// `FamilyName::parse` is based on `SingleFontFamily::parse` and not the other
|
|
/// way around because we want the former to exclude generic family keywords.
|
|
impl Parse for FamilyName {
|
|
fn parse<'i, 't>(
|
|
context: &ParserContext,
|
|
input: &mut Parser<'i, 't>,
|
|
) -> Result<Self, ParseError<'i>> {
|
|
match SingleFontFamily::parse(context, input) {
|
|
Ok(SingleFontFamily::FamilyName(name)) => Ok(name),
|
|
Ok(SingleFontFamily::Generic(_)) => {
|
|
Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
|
},
|
|
Err(e) => Err(e),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// A factor for one of the font-size-adjust metrics, which may be either a number
|
|
/// or the `from-font` keyword.
|
|
#[derive(
|
|
Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem,
|
|
)]
|
|
pub enum FontSizeAdjustFactor {
|
|
/// An explicitly-specified number.
|
|
Number(NonNegativeNumber),
|
|
/// The from-font keyword: resolve the number from font metrics.
|
|
FromFont,
|
|
}
|
|
|
|
/// Specified value for font-size-adjust, intended to help
|
|
/// preserve the readability of text when font fallback occurs.
|
|
///
|
|
/// https://drafts.csswg.org/css-fonts-5/#font-size-adjust-prop
|
|
pub type FontSizeAdjust = generics::GenericFontSizeAdjust<FontSizeAdjustFactor>;
|
|
|
|
impl Parse for FontSizeAdjust {
|
|
fn parse<'i, 't>(
|
|
context: &ParserContext,
|
|
input: &mut Parser<'i, 't>,
|
|
) -> Result<Self, ParseError<'i>> {
|
|
let location = input.current_source_location();
|
|
// First check if we have an adjustment factor without a metrics-basis keyword.
|
|
if let Ok(factor) = input.try_parse(|i| FontSizeAdjustFactor::parse(context, i)) {
|
|
return Ok(Self::ExHeight(factor));
|
|
}
|
|
|
|
let ident = input.expect_ident()?;
|
|
let basis = match_ignore_ascii_case! { &ident,
|
|
"none" => return Ok(Self::None),
|
|
// Check for size adjustment basis keywords.
|
|
"ex-height" => Self::ExHeight,
|
|
"cap-height" => Self::CapHeight,
|
|
"ch-width" => Self::ChWidth,
|
|
"ic-width" => Self::IcWidth,
|
|
"ic-height" => Self::IcHeight,
|
|
// Unknown keyword.
|
|
_ => return Err(location.new_custom_error(
|
|
SelectorParseErrorKind::UnexpectedIdent(ident.clone())
|
|
)),
|
|
};
|
|
|
|
Ok(basis(FontSizeAdjustFactor::parse(context, input)?))
|
|
}
|
|
}
|
|
|
|
/// This is the ratio applied for font-size: larger
|
|
/// and smaller by both Firefox and Chrome
|
|
const LARGER_FONT_SIZE_RATIO: f32 = 1.2;
|
|
|
|
/// The default font size.
|
|
pub const FONT_MEDIUM_PX: f32 = 16.0;
|
|
/// The default line height.
|
|
pub const FONT_MEDIUM_LINE_HEIGHT_PX: f32 = FONT_MEDIUM_PX * 1.2;
|
|
|
|
impl FontSizeKeyword {
|
|
#[inline]
|
|
fn to_length(&self, cx: &Context) -> NonNegativeLength {
|
|
let font = cx.style().get_font();
|
|
|
|
#[cfg(feature = "servo")]
|
|
let family = &font.font_family.families;
|
|
#[cfg(feature = "gecko")]
|
|
let family = &font.mFont.family.families;
|
|
|
|
let generic = family
|
|
.single_generic()
|
|
.unwrap_or(computed::GenericFontFamily::None);
|
|
|
|
#[cfg(feature = "gecko")]
|
|
let base_size = unsafe {
|
|
Atom::with(font.mLanguage.mRawPtr, |language| {
|
|
cx.device().base_size_for_generic(language, generic)
|
|
})
|
|
};
|
|
#[cfg(feature = "servo")]
|
|
let base_size = cx.device().base_size_for_generic(generic);
|
|
|
|
self.to_length_without_context(cx.quirks_mode, base_size)
|
|
}
|
|
|
|
/// Resolve a keyword length without any context, with explicit arguments.
|
|
#[inline]
|
|
pub fn to_length_without_context(
|
|
&self,
|
|
quirks_mode: QuirksMode,
|
|
base_size: Length,
|
|
) -> NonNegativeLength {
|
|
#[cfg(feature = "gecko")]
|
|
debug_assert_ne!(*self, FontSizeKeyword::Math);
|
|
// The tables in this function are originally from
|
|
// nsRuleNode::CalcFontPointSize in Gecko:
|
|
//
|
|
// https://searchfox.org/mozilla-central/rev/c05d9d61188d32b8/layout/style/nsRuleNode.cpp#3150
|
|
//
|
|
// Mapping from base size and HTML size to pixels
|
|
// The first index is (base_size - 9), the second is the
|
|
// HTML size. "0" is CSS keyword xx-small, not HTML size 0,
|
|
// since HTML size 0 is the same as 1.
|
|
//
|
|
// xxs xs s m l xl xxl -
|
|
// - 0/1 2 3 4 5 6 7
|
|
static FONT_SIZE_MAPPING: [[i32; 8]; 8] = [
|
|
[9, 9, 9, 9, 11, 14, 18, 27],
|
|
[9, 9, 9, 10, 12, 15, 20, 30],
|
|
[9, 9, 10, 11, 13, 17, 22, 33],
|
|
[9, 9, 10, 12, 14, 18, 24, 36],
|
|
[9, 10, 12, 13, 16, 20, 26, 39],
|
|
[9, 10, 12, 14, 17, 21, 28, 42],
|
|
[9, 10, 13, 15, 18, 23, 30, 45],
|
|
[9, 10, 13, 16, 18, 24, 32, 48],
|
|
];
|
|
|
|
// This table gives us compatibility with WinNav4 for the default fonts only.
|
|
// In WinNav4, the default fonts were:
|
|
//
|
|
// Times/12pt == Times/16px at 96ppi
|
|
// Courier/10pt == Courier/13px at 96ppi
|
|
//
|
|
// xxs xs s m l xl xxl -
|
|
// - 1 2 3 4 5 6 7
|
|
static QUIRKS_FONT_SIZE_MAPPING: [[i32; 8]; 8] = [
|
|
[9, 9, 9, 9, 11, 14, 18, 28],
|
|
[9, 9, 9, 10, 12, 15, 20, 31],
|
|
[9, 9, 9, 11, 13, 17, 22, 34],
|
|
[9, 9, 10, 12, 14, 18, 24, 37],
|
|
[9, 9, 10, 13, 16, 20, 26, 40],
|
|
[9, 9, 11, 14, 17, 21, 28, 42],
|
|
[9, 10, 12, 15, 17, 23, 30, 45],
|
|
[9, 10, 13, 16, 18, 24, 32, 48],
|
|
];
|
|
|
|
static FONT_SIZE_FACTORS: [i32; 8] = [60, 75, 89, 100, 120, 150, 200, 300];
|
|
let base_size_px = base_size.px().round() as i32;
|
|
let html_size = self.html_size() as usize;
|
|
NonNegative(if base_size_px >= 9 && base_size_px <= 16 {
|
|
let mapping = if quirks_mode == QuirksMode::Quirks {
|
|
QUIRKS_FONT_SIZE_MAPPING
|
|
} else {
|
|
FONT_SIZE_MAPPING
|
|
};
|
|
Length::new(mapping[(base_size_px - 9) as usize][html_size] as f32)
|
|
} else {
|
|
base_size * FONT_SIZE_FACTORS[html_size] as f32 / 100.0
|
|
})
|
|
}
|
|
}
|
|
|
|
impl FontSize {
|
|
/// <https://html.spec.whatwg.org/multipage/#rules-for-parsing-a-legacy-font-size>
|
|
pub fn from_html_size(size: u8) -> Self {
|
|
FontSize::Keyword(KeywordInfo::new(match size {
|
|
// If value is less than 1, let it be 1.
|
|
0 | 1 => FontSizeKeyword::XSmall,
|
|
2 => FontSizeKeyword::Small,
|
|
3 => FontSizeKeyword::Medium,
|
|
4 => FontSizeKeyword::Large,
|
|
5 => FontSizeKeyword::XLarge,
|
|
6 => FontSizeKeyword::XXLarge,
|
|
// If value is greater than 7, let it be 7.
|
|
_ => FontSizeKeyword::XXXLarge,
|
|
}))
|
|
}
|
|
|
|
/// Compute it against a given base font size
|
|
pub fn to_computed_value_against(
|
|
&self,
|
|
context: &Context,
|
|
base_size: FontBaseSize,
|
|
line_height_base: LineHeightBase,
|
|
) -> computed::FontSize {
|
|
let compose_keyword = |factor| {
|
|
context
|
|
.style()
|
|
.get_parent_font()
|
|
.clone_font_size()
|
|
.keyword_info
|
|
.compose(factor)
|
|
};
|
|
let mut info = KeywordInfo::none();
|
|
let size = match *self {
|
|
FontSize::Length(LengthPercentage::Length(ref l)) => {
|
|
if let NoCalcLength::FontRelative(ref value) = *l {
|
|
if let FontRelativeLength::Em(em) = *value {
|
|
// If the parent font was keyword-derived, this is
|
|
// too. Tack the em unit onto the factor
|
|
info = compose_keyword(em);
|
|
}
|
|
}
|
|
let result =
|
|
l.to_computed_value_with_base_size(context, base_size, line_height_base);
|
|
if l.should_zoom_text() {
|
|
context.maybe_zoom_text(result)
|
|
} else {
|
|
result
|
|
}
|
|
},
|
|
FontSize::Length(LengthPercentage::Percentage(pc)) => {
|
|
// If the parent font was keyword-derived, this is too.
|
|
// Tack the % onto the factor
|
|
info = compose_keyword(pc.0);
|
|
(base_size.resolve(context).computed_size() * pc.0).normalized()
|
|
},
|
|
FontSize::Length(LengthPercentage::Calc(ref calc)) => {
|
|
let calc = calc.to_computed_value_zoomed(context, base_size, line_height_base);
|
|
calc.resolve(base_size.resolve(context).computed_size())
|
|
},
|
|
FontSize::Keyword(i) => {
|
|
if i.kw.is_math() {
|
|
// Scaling is done in recompute_math_font_size_if_needed().
|
|
info = compose_keyword(1.);
|
|
// i.kw will always be FontSizeKeyword::Math here. But writing it this
|
|
// allows this code to compile for servo where the Math variant is cfg'd out.
|
|
info.kw = i.kw;
|
|
FontRelativeLength::Em(1.).to_computed_value(
|
|
context,
|
|
base_size,
|
|
line_height_base,
|
|
)
|
|
} else {
|
|
// As a specified keyword, this is keyword derived
|
|
info = i;
|
|
i.to_computed_value(context).clamp_to_non_negative()
|
|
}
|
|
},
|
|
FontSize::Smaller => {
|
|
info = compose_keyword(1. / LARGER_FONT_SIZE_RATIO);
|
|
FontRelativeLength::Em(1. / LARGER_FONT_SIZE_RATIO).to_computed_value(
|
|
context,
|
|
base_size,
|
|
line_height_base,
|
|
)
|
|
},
|
|
FontSize::Larger => {
|
|
info = compose_keyword(LARGER_FONT_SIZE_RATIO);
|
|
FontRelativeLength::Em(LARGER_FONT_SIZE_RATIO).to_computed_value(
|
|
context,
|
|
base_size,
|
|
line_height_base,
|
|
)
|
|
},
|
|
|
|
FontSize::System(_) => {
|
|
#[cfg(feature = "servo")]
|
|
{
|
|
unreachable!()
|
|
}
|
|
#[cfg(feature = "gecko")]
|
|
{
|
|
context
|
|
.cached_system_font
|
|
.as_ref()
|
|
.unwrap()
|
|
.font_size
|
|
.computed_size()
|
|
}
|
|
},
|
|
};
|
|
computed::FontSize {
|
|
computed_size: NonNegative(size),
|
|
used_size: NonNegative(size),
|
|
keyword_info: info,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl ToComputedValue for FontSize {
|
|
type ComputedValue = computed::FontSize;
|
|
|
|
#[inline]
|
|
fn to_computed_value(&self, context: &Context) -> computed::FontSize {
|
|
self.to_computed_value_against(
|
|
context,
|
|
FontBaseSize::InheritedStyle,
|
|
LineHeightBase::InheritedStyle,
|
|
)
|
|
}
|
|
|
|
#[inline]
|
|
fn from_computed_value(computed: &computed::FontSize) -> Self {
|
|
FontSize::Length(LengthPercentage::Length(
|
|
ToComputedValue::from_computed_value(&computed.computed_size()),
|
|
))
|
|
}
|
|
}
|
|
|
|
impl FontSize {
|
|
system_font_methods!(FontSize);
|
|
|
|
/// Get initial value for specified font size.
|
|
#[inline]
|
|
pub fn medium() -> Self {
|
|
FontSize::Keyword(KeywordInfo::medium())
|
|
}
|
|
|
|
/// Parses a font-size, with quirks.
|
|
pub fn parse_quirky<'i, 't>(
|
|
context: &ParserContext,
|
|
input: &mut Parser<'i, 't>,
|
|
allow_quirks: AllowQuirks,
|
|
) -> Result<FontSize, ParseError<'i>> {
|
|
if let Ok(lp) = input
|
|
.try_parse(|i| LengthPercentage::parse_non_negative_quirky(context, i, allow_quirks))
|
|
{
|
|
return Ok(FontSize::Length(lp));
|
|
}
|
|
|
|
if let Ok(kw) = input.try_parse(|i| FontSizeKeyword::parse(i)) {
|
|
return Ok(FontSize::Keyword(KeywordInfo::new(kw)));
|
|
}
|
|
|
|
try_match_ident_ignore_ascii_case! { input,
|
|
"smaller" => Ok(FontSize::Smaller),
|
|
"larger" => Ok(FontSize::Larger),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Parse for FontSize {
|
|
/// <length> | <percentage> | <absolute-size> | <relative-size>
|
|
fn parse<'i, 't>(
|
|
context: &ParserContext,
|
|
input: &mut Parser<'i, 't>,
|
|
) -> Result<FontSize, ParseError<'i>> {
|
|
FontSize::parse_quirky(context, input, AllowQuirks::No)
|
|
}
|
|
}
|
|
|
|
bitflags! {
|
|
#[derive(Clone, Copy)]
|
|
/// Flags of variant alternates in bit
|
|
struct VariantAlternatesParsingFlags: u8 {
|
|
/// None of variant alternates enabled
|
|
const NORMAL = 0;
|
|
/// Historical forms
|
|
const HISTORICAL_FORMS = 0x01;
|
|
/// Stylistic Alternates
|
|
const STYLISTIC = 0x02;
|
|
/// Stylistic Sets
|
|
const STYLESET = 0x04;
|
|
/// Character Variant
|
|
const CHARACTER_VARIANT = 0x08;
|
|
/// Swash glyphs
|
|
const SWASH = 0x10;
|
|
/// Ornaments glyphs
|
|
const ORNAMENTS = 0x20;
|
|
/// Annotation forms
|
|
const ANNOTATION = 0x40;
|
|
}
|
|
}
|
|
|
|
#[derive(
|
|
Clone,
|
|
Debug,
|
|
MallocSizeOf,
|
|
PartialEq,
|
|
SpecifiedValueInfo,
|
|
ToCss,
|
|
ToComputedValue,
|
|
ToResolvedValue,
|
|
ToShmem,
|
|
)]
|
|
#[repr(C, u8)]
|
|
/// Set of variant alternates
|
|
pub enum VariantAlternates {
|
|
/// Enables display of stylistic alternates
|
|
#[css(function)]
|
|
Stylistic(CustomIdent),
|
|
/// Enables display with stylistic sets
|
|
#[css(comma, function)]
|
|
Styleset(#[css(iterable)] crate::OwnedSlice<CustomIdent>),
|
|
/// Enables display of specific character variants
|
|
#[css(comma, function)]
|
|
CharacterVariant(#[css(iterable)] crate::OwnedSlice<CustomIdent>),
|
|
/// Enables display of swash glyphs
|
|
#[css(function)]
|
|
Swash(CustomIdent),
|
|
/// Enables replacement of default glyphs with ornaments
|
|
#[css(function)]
|
|
Ornaments(CustomIdent),
|
|
/// Enables display of alternate annotation forms
|
|
#[css(function)]
|
|
Annotation(CustomIdent),
|
|
/// Enables display of historical forms
|
|
HistoricalForms,
|
|
}
|
|
|
|
#[derive(
|
|
Clone,
|
|
Debug,
|
|
Default,
|
|
MallocSizeOf,
|
|
PartialEq,
|
|
SpecifiedValueInfo,
|
|
ToComputedValue,
|
|
ToCss,
|
|
ToResolvedValue,
|
|
ToShmem,
|
|
)]
|
|
#[repr(transparent)]
|
|
/// List of Variant Alternates
|
|
pub struct FontVariantAlternates(
|
|
#[css(if_empty = "normal", iterable)] crate::OwnedSlice<VariantAlternates>,
|
|
);
|
|
|
|
impl FontVariantAlternates {
|
|
/// Returns the length of all variant alternates.
|
|
pub fn len(&self) -> usize {
|
|
self.0.iter().fold(0, |acc, alternate| match *alternate {
|
|
VariantAlternates::Swash(_) |
|
|
VariantAlternates::Stylistic(_) |
|
|
VariantAlternates::Ornaments(_) |
|
|
VariantAlternates::Annotation(_) => acc + 1,
|
|
VariantAlternates::Styleset(ref slice) |
|
|
VariantAlternates::CharacterVariant(ref slice) => acc + slice.len(),
|
|
_ => acc,
|
|
})
|
|
}
|
|
}
|
|
|
|
impl FontVariantAlternates {
|
|
#[inline]
|
|
/// Get initial specified value with VariantAlternatesList
|
|
pub fn get_initial_specified_value() -> Self {
|
|
Default::default()
|
|
}
|
|
}
|
|
|
|
impl Parse for FontVariantAlternates {
|
|
/// normal |
|
|
/// [ stylistic(<feature-value-name>) ||
|
|
/// historical-forms ||
|
|
/// styleset(<feature-value-name> #) ||
|
|
/// character-variant(<feature-value-name> #) ||
|
|
/// swash(<feature-value-name>) ||
|
|
/// ornaments(<feature-value-name>) ||
|
|
/// annotation(<feature-value-name>) ]
|
|
fn parse<'i, 't>(
|
|
_: &ParserContext,
|
|
input: &mut Parser<'i, 't>,
|
|
) -> Result<FontVariantAlternates, ParseError<'i>> {
|
|
if input
|
|
.try_parse(|input| input.expect_ident_matching("normal"))
|
|
.is_ok()
|
|
{
|
|
return Ok(Default::default());
|
|
}
|
|
|
|
let mut stylistic = None;
|
|
let mut historical = None;
|
|
let mut styleset = None;
|
|
let mut character_variant = None;
|
|
let mut swash = None;
|
|
let mut ornaments = None;
|
|
let mut annotation = None;
|
|
|
|
// Parse values for the various alternate types in any order.
|
|
let mut parsed_alternates = VariantAlternatesParsingFlags::empty();
|
|
macro_rules! check_if_parsed(
|
|
($input:expr, $flag:path) => (
|
|
if parsed_alternates.contains($flag) {
|
|
return Err($input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
|
}
|
|
parsed_alternates |= $flag;
|
|
)
|
|
);
|
|
while let Ok(_) = input.try_parse(|input| match *input.next()? {
|
|
Token::Ident(ref value) if value.eq_ignore_ascii_case("historical-forms") => {
|
|
check_if_parsed!(input, VariantAlternatesParsingFlags::HISTORICAL_FORMS);
|
|
historical = Some(VariantAlternates::HistoricalForms);
|
|
Ok(())
|
|
},
|
|
Token::Function(ref name) => {
|
|
let name = name.clone();
|
|
input.parse_nested_block(|i| {
|
|
match_ignore_ascii_case! { &name,
|
|
"swash" => {
|
|
check_if_parsed!(i, VariantAlternatesParsingFlags::SWASH);
|
|
let ident = CustomIdent::parse(i, &[])?;
|
|
swash = Some(VariantAlternates::Swash(ident));
|
|
Ok(())
|
|
},
|
|
"stylistic" => {
|
|
check_if_parsed!(i, VariantAlternatesParsingFlags::STYLISTIC);
|
|
let ident = CustomIdent::parse(i, &[])?;
|
|
stylistic = Some(VariantAlternates::Stylistic(ident));
|
|
Ok(())
|
|
},
|
|
"ornaments" => {
|
|
check_if_parsed!(i, VariantAlternatesParsingFlags::ORNAMENTS);
|
|
let ident = CustomIdent::parse(i, &[])?;
|
|
ornaments = Some(VariantAlternates::Ornaments(ident));
|
|
Ok(())
|
|
},
|
|
"annotation" => {
|
|
check_if_parsed!(i, VariantAlternatesParsingFlags::ANNOTATION);
|
|
let ident = CustomIdent::parse(i, &[])?;
|
|
annotation = Some(VariantAlternates::Annotation(ident));
|
|
Ok(())
|
|
},
|
|
"styleset" => {
|
|
check_if_parsed!(i, VariantAlternatesParsingFlags::STYLESET);
|
|
let idents = i.parse_comma_separated(|i| {
|
|
CustomIdent::parse(i, &[])
|
|
})?;
|
|
styleset = Some(VariantAlternates::Styleset(idents.into()));
|
|
Ok(())
|
|
},
|
|
"character-variant" => {
|
|
check_if_parsed!(i, VariantAlternatesParsingFlags::CHARACTER_VARIANT);
|
|
let idents = i.parse_comma_separated(|i| {
|
|
CustomIdent::parse(i, &[])
|
|
})?;
|
|
character_variant = Some(VariantAlternates::CharacterVariant(idents.into()));
|
|
Ok(())
|
|
},
|
|
_ => return Err(i.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
|
|
}
|
|
})
|
|
},
|
|
_ => Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
|
|
}) {}
|
|
|
|
if parsed_alternates.is_empty() {
|
|
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
|
|
}
|
|
|
|
// Collect the parsed values in canonical order, so that we'll serialize correctly.
|
|
let mut alternates = Vec::new();
|
|
macro_rules! push_if_some(
|
|
($value:expr) => (
|
|
if let Some(v) = $value {
|
|
alternates.push(v);
|
|
}
|
|
)
|
|
);
|
|
push_if_some!(stylistic);
|
|
push_if_some!(historical);
|
|
push_if_some!(styleset);
|
|
push_if_some!(character_variant);
|
|
push_if_some!(swash);
|
|
push_if_some!(ornaments);
|
|
push_if_some!(annotation);
|
|
|
|
Ok(FontVariantAlternates(alternates.into()))
|
|
}
|
|
}
|
|
|
|
#[derive(
|
|
Clone,
|
|
Copy,
|
|
Debug,
|
|
Eq,
|
|
MallocSizeOf,
|
|
PartialEq,
|
|
Parse,
|
|
SpecifiedValueInfo,
|
|
ToComputedValue,
|
|
ToCss,
|
|
ToResolvedValue,
|
|
ToShmem,
|
|
)]
|
|
#[css(bitflags(
|
|
single = "normal",
|
|
mixed = "jis78,jis83,jis90,jis04,simplified,traditional,full-width,proportional-width,ruby",
|
|
validate_mixed = "Self::validate_mixed_flags",
|
|
))]
|
|
#[repr(C)]
|
|
/// Variants for east asian variant
|
|
pub struct FontVariantEastAsian(u16);
|
|
bitflags! {
|
|
impl FontVariantEastAsian: u16 {
|
|
/// None of the features
|
|
const NORMAL = 0;
|
|
/// Enables rendering of JIS78 forms (OpenType feature: jp78)
|
|
const JIS78 = 1 << 0;
|
|
/// Enables rendering of JIS83 forms (OpenType feature: jp83).
|
|
const JIS83 = 1 << 1;
|
|
/// Enables rendering of JIS90 forms (OpenType feature: jp90).
|
|
const JIS90 = 1 << 2;
|
|
/// Enables rendering of JIS2004 forms (OpenType feature: jp04).
|
|
const JIS04 = 1 << 3;
|
|
/// Enables rendering of simplified forms (OpenType feature: smpl).
|
|
const SIMPLIFIED = 1 << 4;
|
|
/// Enables rendering of traditional forms (OpenType feature: trad).
|
|
const TRADITIONAL = 1 << 5;
|
|
|
|
/// These values are exclusive with each other.
|
|
const JIS_GROUP = Self::JIS78.0 | Self::JIS83.0 | Self::JIS90.0 | Self::JIS04.0 | Self::SIMPLIFIED.0 | Self::TRADITIONAL.0;
|
|
|
|
/// Enables rendering of full-width variants (OpenType feature: fwid).
|
|
const FULL_WIDTH = 1 << 6;
|
|
/// Enables rendering of proportionally-spaced variants (OpenType feature: pwid).
|
|
const PROPORTIONAL_WIDTH = 1 << 7;
|
|
/// Enables display of ruby variant glyphs (OpenType feature: ruby).
|
|
const RUBY = 1 << 8;
|
|
}
|
|
}
|
|
|
|
impl FontVariantEastAsian {
|
|
/// The number of variants.
|
|
pub const COUNT: usize = 9;
|
|
|
|
fn validate_mixed_flags(&self) -> bool {
|
|
if self.contains(Self::FULL_WIDTH | Self::PROPORTIONAL_WIDTH) {
|
|
// full-width and proportional-width are exclusive with each other.
|
|
return false;
|
|
}
|
|
let jis = self.intersection(Self::JIS_GROUP);
|
|
if !jis.is_empty() && !jis.bits().is_power_of_two() {
|
|
return false;
|
|
}
|
|
true
|
|
}
|
|
}
|
|
|
|
#[derive(
|
|
Clone,
|
|
Copy,
|
|
Debug,
|
|
Eq,
|
|
MallocSizeOf,
|
|
PartialEq,
|
|
Parse,
|
|
SpecifiedValueInfo,
|
|
ToComputedValue,
|
|
ToCss,
|
|
ToResolvedValue,
|
|
ToShmem,
|
|
)]
|
|
#[css(bitflags(
|
|
single = "normal,none",
|
|
mixed = "common-ligatures,no-common-ligatures,discretionary-ligatures,no-discretionary-ligatures,historical-ligatures,no-historical-ligatures,contextual,no-contextual",
|
|
validate_mixed = "Self::validate_mixed_flags",
|
|
))]
|
|
#[repr(C)]
|
|
/// Variants of ligatures
|
|
pub struct FontVariantLigatures(u16);
|
|
bitflags! {
|
|
impl FontVariantLigatures: u16 {
|
|
/// Specifies that common default features are enabled
|
|
const NORMAL = 0;
|
|
/// Specifies that no features are enabled;
|
|
const NONE = 1;
|
|
/// Enables display of common ligatures
|
|
const COMMON_LIGATURES = 1 << 1;
|
|
/// Disables display of common ligatures
|
|
const NO_COMMON_LIGATURES = 1 << 2;
|
|
/// Enables display of discretionary ligatures
|
|
const DISCRETIONARY_LIGATURES = 1 << 3;
|
|
/// Disables display of discretionary ligatures
|
|
const NO_DISCRETIONARY_LIGATURES = 1 << 4;
|
|
/// Enables display of historical ligatures
|
|
const HISTORICAL_LIGATURES = 1 << 5;
|
|
/// Disables display of historical ligatures
|
|
const NO_HISTORICAL_LIGATURES = 1 << 6;
|
|
/// Enables display of contextual alternates
|
|
const CONTEXTUAL = 1 << 7;
|
|
/// Disables display of contextual alternates
|
|
const NO_CONTEXTUAL = 1 << 8;
|
|
}
|
|
}
|
|
|
|
impl FontVariantLigatures {
|
|
/// The number of variants.
|
|
pub const COUNT: usize = 9;
|
|
|
|
fn validate_mixed_flags(&self) -> bool {
|
|
// Mixing a value and its disabling value is forbidden.
|
|
if self.contains(Self::COMMON_LIGATURES | Self::NO_COMMON_LIGATURES) ||
|
|
self.contains(Self::DISCRETIONARY_LIGATURES | Self::NO_DISCRETIONARY_LIGATURES) ||
|
|
self.contains(Self::HISTORICAL_LIGATURES | Self::NO_HISTORICAL_LIGATURES) ||
|
|
self.contains(Self::CONTEXTUAL | Self::NO_CONTEXTUAL)
|
|
{
|
|
return false;
|
|
}
|
|
true
|
|
}
|
|
}
|
|
|
|
/// Variants of numeric values
|
|
#[derive(
|
|
Clone,
|
|
Copy,
|
|
Debug,
|
|
Eq,
|
|
MallocSizeOf,
|
|
PartialEq,
|
|
Parse,
|
|
SpecifiedValueInfo,
|
|
ToComputedValue,
|
|
ToCss,
|
|
ToResolvedValue,
|
|
ToShmem,
|
|
)]
|
|
#[css(bitflags(
|
|
single = "normal",
|
|
mixed = "lining-nums,oldstyle-nums,proportional-nums,tabular-nums,diagonal-fractions,stacked-fractions,ordinal,slashed-zero",
|
|
validate_mixed = "Self::validate_mixed_flags",
|
|
))]
|
|
#[repr(C)]
|
|
pub struct FontVariantNumeric(u8);
|
|
bitflags! {
|
|
impl FontVariantNumeric : u8 {
|
|
/// Specifies that common default features are enabled
|
|
const NORMAL = 0;
|
|
/// Enables display of lining numerals.
|
|
const LINING_NUMS = 1 << 0;
|
|
/// Enables display of old-style numerals.
|
|
const OLDSTYLE_NUMS = 1 << 1;
|
|
/// Enables display of proportional numerals.
|
|
const PROPORTIONAL_NUMS = 1 << 2;
|
|
/// Enables display of tabular numerals.
|
|
const TABULAR_NUMS = 1 << 3;
|
|
/// Enables display of lining diagonal fractions.
|
|
const DIAGONAL_FRACTIONS = 1 << 4;
|
|
/// Enables display of lining stacked fractions.
|
|
const STACKED_FRACTIONS = 1 << 5;
|
|
/// Enables display of slashed zeros.
|
|
const SLASHED_ZERO = 1 << 6;
|
|
/// Enables display of letter forms used with ordinal numbers.
|
|
const ORDINAL = 1 << 7;
|
|
}
|
|
}
|
|
|
|
impl FontVariantNumeric {
|
|
/// The number of variants.
|
|
pub const COUNT: usize = 8;
|
|
|
|
/// normal |
|
|
/// [ <numeric-figure-values> ||
|
|
/// <numeric-spacing-values> ||
|
|
/// <numeric-fraction-values> ||
|
|
/// ordinal ||
|
|
/// slashed-zero ]
|
|
/// <numeric-figure-values> = [ lining-nums | oldstyle-nums ]
|
|
/// <numeric-spacing-values> = [ proportional-nums | tabular-nums ]
|
|
/// <numeric-fraction-values> = [ diagonal-fractions | stacked-fractions ]
|
|
fn validate_mixed_flags(&self) -> bool {
|
|
if self.contains(Self::LINING_NUMS | Self::OLDSTYLE_NUMS) ||
|
|
self.contains(Self::PROPORTIONAL_NUMS | Self::TABULAR_NUMS) ||
|
|
self.contains(Self::DIAGONAL_FRACTIONS | Self::STACKED_FRACTIONS)
|
|
{
|
|
return false;
|
|
}
|
|
true
|
|
}
|
|
}
|
|
|
|
/// This property provides low-level control over OpenType or TrueType font features.
|
|
pub type FontFeatureSettings = FontSettings<FeatureTagValue<Integer>>;
|
|
|
|
/// For font-language-override, use the same representation as the computed value.
|
|
pub use crate::values::computed::font::FontLanguageOverride;
|
|
|
|
impl Parse for FontLanguageOverride {
|
|
/// normal | <string>
|
|
fn parse<'i, 't>(
|
|
_: &ParserContext,
|
|
input: &mut Parser<'i, 't>,
|
|
) -> Result<FontLanguageOverride, ParseError<'i>> {
|
|
if input
|
|
.try_parse(|input| input.expect_ident_matching("normal"))
|
|
.is_ok()
|
|
{
|
|
return Ok(FontLanguageOverride::normal());
|
|
}
|
|
|
|
let string = input.expect_string()?;
|
|
|
|
// The OpenType spec requires tags to be 1 to 4 ASCII characters:
|
|
// https://learn.microsoft.com/en-gb/typography/opentype/spec/otff#data-types
|
|
if string.is_empty() || string.len() > 4 || !string.is_ascii() {
|
|
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
|
|
}
|
|
|
|
let mut bytes = [b' '; 4];
|
|
for (byte, str_byte) in bytes.iter_mut().zip(string.as_bytes()) {
|
|
*byte = *str_byte;
|
|
}
|
|
|
|
Ok(FontLanguageOverride(u32::from_be_bytes(bytes)))
|
|
}
|
|
}
|
|
|
|
/// A value for any of the font-synthesis-{weight,small-caps,position} properties.
|
|
#[repr(u8)]
|
|
#[derive(
|
|
Clone,
|
|
Copy,
|
|
Debug,
|
|
Eq,
|
|
MallocSizeOf,
|
|
Parse,
|
|
PartialEq,
|
|
SpecifiedValueInfo,
|
|
ToComputedValue,
|
|
ToCss,
|
|
ToResolvedValue,
|
|
ToShmem,
|
|
)]
|
|
pub enum FontSynthesis {
|
|
/// This attribute may be synthesized if not supported by a face.
|
|
Auto,
|
|
/// Do not attempt to synthesis this style attribute.
|
|
None,
|
|
}
|
|
|
|
/// A value for the font-synthesis-style property.
|
|
#[repr(u8)]
|
|
#[derive(
|
|
Clone,
|
|
Copy,
|
|
Debug,
|
|
Eq,
|
|
MallocSizeOf,
|
|
Parse,
|
|
PartialEq,
|
|
SpecifiedValueInfo,
|
|
ToComputedValue,
|
|
ToCss,
|
|
ToResolvedValue,
|
|
ToShmem,
|
|
)]
|
|
pub enum FontSynthesisStyle {
|
|
/// This attribute may be synthesized if not supported by a face.
|
|
Auto,
|
|
/// Do not attempt to synthesis this style attribute.
|
|
None,
|
|
/// Allow synthesis for oblique, but not for italic.
|
|
ObliqueOnly,
|
|
}
|
|
|
|
#[derive(
|
|
Clone,
|
|
Debug,
|
|
Eq,
|
|
MallocSizeOf,
|
|
PartialEq,
|
|
SpecifiedValueInfo,
|
|
ToComputedValue,
|
|
ToResolvedValue,
|
|
ToShmem,
|
|
)]
|
|
#[repr(C)]
|
|
/// Allows authors to choose a palette from those supported by a color font
|
|
/// (and potentially @font-palette-values overrides).
|
|
pub struct FontPalette(Atom);
|
|
|
|
#[allow(missing_docs)]
|
|
impl FontPalette {
|
|
pub fn normal() -> Self {
|
|
Self(atom!("normal"))
|
|
}
|
|
pub fn light() -> Self {
|
|
Self(atom!("light"))
|
|
}
|
|
pub fn dark() -> Self {
|
|
Self(atom!("dark"))
|
|
}
|
|
}
|
|
|
|
impl Parse for FontPalette {
|
|
/// normal | light | dark | dashed-ident
|
|
fn parse<'i, 't>(
|
|
_context: &ParserContext,
|
|
input: &mut Parser<'i, 't>,
|
|
) -> Result<FontPalette, ParseError<'i>> {
|
|
let location = input.current_source_location();
|
|
let ident = input.expect_ident()?;
|
|
match_ignore_ascii_case! { &ident,
|
|
"normal" => Ok(Self::normal()),
|
|
"light" => Ok(Self::light()),
|
|
"dark" => Ok(Self::dark()),
|
|
_ => if ident.starts_with("--") {
|
|
Ok(Self(Atom::from(ident.as_ref())))
|
|
} else {
|
|
Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(ident.clone())))
|
|
},
|
|
}
|
|
}
|
|
}
|
|
|
|
impl ToCss for FontPalette {
|
|
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
|
|
where
|
|
W: Write,
|
|
{
|
|
serialize_atom_identifier(&self.0, dest)
|
|
}
|
|
}
|
|
|
|
/// This property provides low-level control over OpenType or TrueType font
|
|
/// variations.
|
|
pub type FontVariationSettings = FontSettings<VariationValue<Number>>;
|
|
|
|
fn parse_one_feature_value<'i, 't>(
|
|
context: &ParserContext,
|
|
input: &mut Parser<'i, 't>,
|
|
) -> Result<Integer, ParseError<'i>> {
|
|
if let Ok(integer) = input.try_parse(|i| Integer::parse_non_negative(context, i)) {
|
|
return Ok(integer);
|
|
}
|
|
|
|
try_match_ident_ignore_ascii_case! { input,
|
|
"on" => Ok(Integer::new(1)),
|
|
"off" => Ok(Integer::new(0)),
|
|
}
|
|
}
|
|
|
|
impl Parse for FeatureTagValue<Integer> {
|
|
/// https://drafts.csswg.org/css-fonts-4/#feature-tag-value
|
|
fn parse<'i, 't>(
|
|
context: &ParserContext,
|
|
input: &mut Parser<'i, 't>,
|
|
) -> Result<Self, ParseError<'i>> {
|
|
let tag = FontTag::parse(context, input)?;
|
|
let value = input
|
|
.try_parse(|i| parse_one_feature_value(context, i))
|
|
.unwrap_or_else(|_| Integer::new(1));
|
|
|
|
Ok(Self { tag, value })
|
|
}
|
|
}
|
|
|
|
impl Parse for VariationValue<Number> {
|
|
/// This is the `<string> <number>` part of the font-variation-settings
|
|
/// syntax.
|
|
fn parse<'i, 't>(
|
|
context: &ParserContext,
|
|
input: &mut Parser<'i, 't>,
|
|
) -> Result<Self, ParseError<'i>> {
|
|
let tag = FontTag::parse(context, input)?;
|
|
let value = Number::parse(context, input)?;
|
|
Ok(Self { tag, value })
|
|
}
|
|
}
|
|
|
|
/// A metrics override value for a @font-face descriptor
|
|
///
|
|
/// https://drafts.csswg.org/css-fonts/#font-metrics-override-desc
|
|
#[derive(
|
|
Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem,
|
|
)]
|
|
pub enum MetricsOverride {
|
|
/// A non-negative `<percentage>` of the computed font size
|
|
Override(NonNegativePercentage),
|
|
/// Normal metrics from the font.
|
|
Normal,
|
|
}
|
|
|
|
impl MetricsOverride {
|
|
#[inline]
|
|
/// Get default value with `normal`
|
|
pub fn normal() -> MetricsOverride {
|
|
MetricsOverride::Normal
|
|
}
|
|
|
|
/// The ToComputedValue implementation, used for @font-face descriptors.
|
|
///
|
|
/// Valid override percentages must be non-negative; we return -1.0 to indicate
|
|
/// the absence of an override (i.e. 'normal').
|
|
#[inline]
|
|
pub fn compute(&self) -> ComputedPercentage {
|
|
match *self {
|
|
MetricsOverride::Normal => ComputedPercentage(-1.0),
|
|
MetricsOverride::Override(percent) => ComputedPercentage(percent.0.get()),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(
|
|
Clone,
|
|
Copy,
|
|
Debug,
|
|
MallocSizeOf,
|
|
Parse,
|
|
PartialEq,
|
|
SpecifiedValueInfo,
|
|
ToComputedValue,
|
|
ToCss,
|
|
ToResolvedValue,
|
|
ToShmem,
|
|
)]
|
|
#[repr(u8)]
|
|
/// How to do font-size scaling.
|
|
pub enum XTextScale {
|
|
/// Both min-font-size and text zoom are enabled.
|
|
All,
|
|
/// Text-only zoom is enabled, but min-font-size is not honored.
|
|
ZoomOnly,
|
|
/// Neither of them is enabled.
|
|
None,
|
|
}
|
|
|
|
impl XTextScale {
|
|
/// Returns whether text zoom is enabled.
|
|
#[inline]
|
|
pub fn text_zoom_enabled(self) -> bool {
|
|
self != Self::None
|
|
}
|
|
}
|
|
|
|
#[derive(
|
|
Clone,
|
|
Debug,
|
|
MallocSizeOf,
|
|
PartialEq,
|
|
SpecifiedValueInfo,
|
|
ToComputedValue,
|
|
ToCss,
|
|
ToResolvedValue,
|
|
ToShmem,
|
|
)]
|
|
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
|
|
/// Internal property that reflects the lang attribute
|
|
pub struct XLang(#[css(skip)] pub Atom);
|
|
|
|
impl XLang {
|
|
#[inline]
|
|
/// Get default value for `-x-lang`
|
|
pub fn get_initial_value() -> XLang {
|
|
XLang(atom!(""))
|
|
}
|
|
}
|
|
|
|
impl Parse for XLang {
|
|
fn parse<'i, 't>(
|
|
_: &ParserContext,
|
|
input: &mut Parser<'i, 't>,
|
|
) -> Result<XLang, ParseError<'i>> {
|
|
debug_assert!(
|
|
false,
|
|
"Should be set directly by presentation attributes only."
|
|
);
|
|
Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
|
}
|
|
}
|
|
|
|
#[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
|
|
#[derive(Clone, Copy, Debug, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
|
|
/// Specifies the minimum font size allowed due to changes in scriptlevel.
|
|
/// Ref: https://wiki.mozilla.org/MathML:mstyle
|
|
pub struct MozScriptMinSize(pub NoCalcLength);
|
|
|
|
impl MozScriptMinSize {
|
|
#[inline]
|
|
/// Calculate initial value of -moz-script-min-size.
|
|
pub fn get_initial_value() -> Length {
|
|
Length::new(DEFAULT_SCRIPT_MIN_SIZE_PT as f32 * PX_PER_PT)
|
|
}
|
|
}
|
|
|
|
impl Parse for MozScriptMinSize {
|
|
fn parse<'i, 't>(
|
|
_: &ParserContext,
|
|
input: &mut Parser<'i, 't>,
|
|
) -> Result<MozScriptMinSize, ParseError<'i>> {
|
|
debug_assert!(
|
|
false,
|
|
"Should be set directly by presentation attributes only."
|
|
);
|
|
Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
|
}
|
|
}
|
|
|
|
/// A value for the `math-depth` property.
|
|
/// https://mathml-refresh.github.io/mathml-core/#the-math-script-level-property
|
|
#[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
|
|
#[derive(Clone, Copy, Debug, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
|
|
pub enum MathDepth {
|
|
/// Increment math-depth if math-style is compact.
|
|
AutoAdd,
|
|
|
|
/// Add the function's argument to math-depth.
|
|
#[css(function)]
|
|
Add(Integer),
|
|
|
|
/// Set math-depth to the specified value.
|
|
Absolute(Integer),
|
|
}
|
|
|
|
impl Parse for MathDepth {
|
|
fn parse<'i, 't>(
|
|
context: &ParserContext,
|
|
input: &mut Parser<'i, 't>,
|
|
) -> Result<MathDepth, ParseError<'i>> {
|
|
if input
|
|
.try_parse(|i| i.expect_ident_matching("auto-add"))
|
|
.is_ok()
|
|
{
|
|
return Ok(MathDepth::AutoAdd);
|
|
}
|
|
if let Ok(math_depth_value) = input.try_parse(|input| Integer::parse(context, input)) {
|
|
return Ok(MathDepth::Absolute(math_depth_value));
|
|
}
|
|
input.expect_function_matching("add")?;
|
|
let math_depth_delta_value =
|
|
input.parse_nested_block(|input| Integer::parse(context, input))?;
|
|
Ok(MathDepth::Add(math_depth_delta_value))
|
|
}
|
|
}
|
|
|
|
#[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
|
|
#[derive(
|
|
Clone,
|
|
Copy,
|
|
Debug,
|
|
PartialEq,
|
|
SpecifiedValueInfo,
|
|
ToComputedValue,
|
|
ToCss,
|
|
ToResolvedValue,
|
|
ToShmem,
|
|
)]
|
|
/// Specifies the multiplier to be used to adjust font size
|
|
/// due to changes in scriptlevel.
|
|
///
|
|
/// Ref: https://www.w3.org/TR/MathML3/chapter3.html#presm.mstyle.attrs
|
|
pub struct MozScriptSizeMultiplier(pub f32);
|
|
|
|
impl MozScriptSizeMultiplier {
|
|
#[inline]
|
|
/// Get default value of `-moz-script-size-multiplier`
|
|
pub fn get_initial_value() -> MozScriptSizeMultiplier {
|
|
MozScriptSizeMultiplier(DEFAULT_SCRIPT_SIZE_MULTIPLIER as f32)
|
|
}
|
|
}
|
|
|
|
impl Parse for MozScriptSizeMultiplier {
|
|
fn parse<'i, 't>(
|
|
_: &ParserContext,
|
|
input: &mut Parser<'i, 't>,
|
|
) -> Result<MozScriptSizeMultiplier, ParseError<'i>> {
|
|
debug_assert!(
|
|
false,
|
|
"Should be set directly by presentation attributes only."
|
|
);
|
|
Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
|
}
|
|
}
|
|
|
|
impl From<f32> for MozScriptSizeMultiplier {
|
|
fn from(v: f32) -> Self {
|
|
MozScriptSizeMultiplier(v)
|
|
}
|
|
}
|
|
|
|
impl From<MozScriptSizeMultiplier> for f32 {
|
|
fn from(v: MozScriptSizeMultiplier) -> f32 {
|
|
v.0
|
|
}
|
|
}
|
|
|
|
/// A specified value for the `line-height` property.
|
|
pub type LineHeight = GenericLineHeight<NonNegativeNumber, NonNegativeLengthPercentage>;
|
|
|
|
impl ToComputedValue for LineHeight {
|
|
type ComputedValue = computed::LineHeight;
|
|
|
|
#[inline]
|
|
fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
|
|
match *self {
|
|
GenericLineHeight::Normal => GenericLineHeight::Normal,
|
|
#[cfg(feature = "gecko")]
|
|
GenericLineHeight::MozBlockHeight => GenericLineHeight::MozBlockHeight,
|
|
GenericLineHeight::Number(number) => {
|
|
GenericLineHeight::Number(number.to_computed_value(context))
|
|
},
|
|
GenericLineHeight::Length(ref non_negative_lp) => {
|
|
let result = match non_negative_lp.0 {
|
|
LengthPercentage::Length(NoCalcLength::Absolute(ref abs)) => {
|
|
context.maybe_zoom_text(abs.to_computed_value(context))
|
|
},
|
|
LengthPercentage::Length(ref length) => {
|
|
// line-height units specifically resolve against parent's
|
|
// font and line-height properties, while the rest of font
|
|
// relative units still resolve against the element's own
|
|
// properties.
|
|
length.to_computed_value_with_base_size(
|
|
context,
|
|
FontBaseSize::CurrentStyle,
|
|
LineHeightBase::InheritedStyle,
|
|
)
|
|
},
|
|
LengthPercentage::Percentage(ref p) => FontRelativeLength::Em(p.0)
|
|
.to_computed_value(
|
|
context,
|
|
FontBaseSize::CurrentStyle,
|
|
LineHeightBase::InheritedStyle,
|
|
),
|
|
LengthPercentage::Calc(ref calc) => {
|
|
let computed_calc = calc.to_computed_value_zoomed(
|
|
context,
|
|
FontBaseSize::CurrentStyle,
|
|
LineHeightBase::InheritedStyle,
|
|
);
|
|
let base = context.style().get_font().clone_font_size().computed_size();
|
|
computed_calc.resolve(base)
|
|
},
|
|
};
|
|
GenericLineHeight::Length(result.into())
|
|
},
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
fn from_computed_value(computed: &Self::ComputedValue) -> Self {
|
|
match *computed {
|
|
GenericLineHeight::Normal => GenericLineHeight::Normal,
|
|
#[cfg(feature = "gecko")]
|
|
GenericLineHeight::MozBlockHeight => GenericLineHeight::MozBlockHeight,
|
|
GenericLineHeight::Number(ref number) => {
|
|
GenericLineHeight::Number(NonNegativeNumber::from_computed_value(number))
|
|
},
|
|
GenericLineHeight::Length(ref length) => {
|
|
GenericLineHeight::Length(NoCalcLength::from_computed_value(&length.0).into())
|
|
},
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Flags for the query_font_metrics() function.
|
|
#[repr(C)]
|
|
pub struct QueryFontMetricsFlags(u8);
|
|
|
|
bitflags! {
|
|
impl QueryFontMetricsFlags: u8 {
|
|
/// Should we use the user font set?
|
|
const USE_USER_FONT_SET = 1 << 0;
|
|
/// Does the caller need the `ch` unit (width of the ZERO glyph)?
|
|
const NEEDS_CH = 1 << 1;
|
|
/// Does the caller need the `ic` unit (width of the WATER ideograph)?
|
|
const NEEDS_IC = 1 << 2;
|
|
/// Does the caller need math scales to be retrieved?
|
|
const NEEDS_MATH_SCALES = 1 << 3;
|
|
}
|
|
}
|