Refactor style::values::specified::Length to store length by kind (absolute, font-relative or character width)

This commit is contained in:
James Gilbertson 2015-03-05 07:01:00 -07:00
parent 00785ecf63
commit 41786c4cb4
3 changed files with 105 additions and 75 deletions

View file

@ -114,17 +114,70 @@ pub mod specified {
}
#[derive(Clone, PartialEq, Copy)]
pub enum Length {
Au(Au), // application units
pub enum FontRelativeLength {
Em(CSSFloat),
Ex(CSSFloat),
Rem(CSSFloat),
Ch(CSSFloat),
Rem(CSSFloat)
}
impl fmt::Debug for FontRelativeLength {
#[inline] fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.fmt_to_css(f) }
}
impl ToCss for FontRelativeLength {
fn to_css<W>(&self, dest: &mut W) -> text_writer::Result where W: TextWriter {
match self {
&FontRelativeLength::Em(length) => write!(dest, "{}em", length),
&FontRelativeLength::Ex(length) => write!(dest, "{}ex", length),
&FontRelativeLength::Ch(length) => write!(dest, "{}ch", length),
&FontRelativeLength::Rem(length) => write!(dest, "{}rem", length)
}
}
}
impl FontRelativeLength {
pub fn to_computed_value(&self,
reference_font_size: Au,
root_font_size: Au)
-> Au
{
match self {
&FontRelativeLength::Em(length) => reference_font_size.scale_by(length),
&FontRelativeLength::Ex(length) => {
let x_height = 0.5; // TODO: find that from the font
reference_font_size.scale_by(length * x_height)
},
&FontRelativeLength::Ch(_) => unimplemented!(),
&FontRelativeLength::Rem(length) => root_font_size.scale_by(length)
}
}
}
#[derive(Clone, PartialEq, Copy)]
pub struct CharacterWidth(pub i32);
impl CharacterWidth {
pub fn to_computed_value(&self, reference_font_size: Au) -> Au {
// This applies the *converting a character width to pixels* algorithm as specified
// in HTML5 § 14.5.4.
//
// TODO(pcwalton): Find these from the font.
let average_advance = reference_font_size.scale_by(0.5);
let max_advance = reference_font_size;
average_advance.scale_by(self.0 as CSSFloat - 1.0) + max_advance
}
}
#[derive(Clone, PartialEq, Copy)]
pub enum Length {
Absolute(Au), // application units
FontRelative(FontRelativeLength),
/// HTML5 "character width", as defined in HTML5 § 14.5.4.
///
/// This cannot be specified by the user directly and is only generated by
/// `Stylist::synthesize_rules_for_legacy_attributes()`.
ServoCharacterWidth(i32),
ServoCharacterWidth(CharacterWidth),
}
impl fmt::Debug for Length {
@ -134,41 +187,14 @@ pub mod specified {
impl ToCss for Length {
fn to_css<W>(&self, dest: &mut W) -> text_writer::Result where W: TextWriter {
match self {
&Length::Au(length) => write!(dest, "{}px", length.to_subpx()),
&Length::Em(length) => write!(dest, "{}em", length),
&Length::Ex(length) => write!(dest, "{}ex", length),
&Length::Rem(length) => write!(dest, "{}rem", length),
&Length::Absolute(length) => write!(dest, "{}px", length.to_subpx()),
&Length::FontRelative(length) => length.to_css(dest),
&Length::ServoCharacterWidth(_)
=> panic!("internal CSS values should never be serialized"),
}
}
}
impl Length {
pub fn to_computed_value_with_font_size(&self, reference_font_size: Au, root_font_size: Au)
-> Au {
match *self {
Length::Au(value) => value,
Length::Em(value) => reference_font_size.scale_by(value),
Length::Ex(value) => {
let x_height = 0.5; // TODO: find that from the font
reference_font_size.scale_by(value * x_height)
},
Length::Rem(value) => root_font_size.scale_by(value),
Length::ServoCharacterWidth(value) => {
// This applies the *converting a character width to pixels* algorithm as specified
// in HTML5 § 14.5.4.
//
// TODO(pcwalton): Find these from the font.
let average_advance = reference_font_size.scale_by(0.5);
let max_advance = reference_font_size;
average_advance.scale_by(value as CSSFloat - 1.0) + max_advance
}
}
}
}
const AU_PER_PX: CSSFloat = 60.;
const AU_PER_IN: CSSFloat = AU_PER_PX * 96.;
const AU_PER_CM: CSSFloat = AU_PER_IN / 2.54;
@ -182,7 +208,7 @@ pub mod specified {
Token::Dimension(ref value, ref unit) if negative_ok || value.value >= 0. => {
Length::parse_dimension(value.value, unit)
}
Token::Number(ref value) if value.value == 0. => Ok(Length::Au(Au(0))),
Token::Number(ref value) if value.value == 0. => Ok(Length::Absolute(Au(0))),
_ => Err(())
}
}
@ -196,20 +222,22 @@ pub mod specified {
pub fn parse_dimension(value: CSSFloat, unit: &str) -> Result<Length, ()> {
match_ignore_ascii_case! { unit,
"px" => Ok(Length::from_px(value)),
"in" => Ok(Length::Au(Au((value * AU_PER_IN) as i32))),
"cm" => Ok(Length::Au(Au((value * AU_PER_CM) as i32))),
"mm" => Ok(Length::Au(Au((value * AU_PER_MM) as i32))),
"pt" => Ok(Length::Au(Au((value * AU_PER_PT) as i32))),
"pc" => Ok(Length::Au(Au((value * AU_PER_PC) as i32))),
"em" => Ok(Length::Em(value)),
"ex" => Ok(Length::Ex(value)),
"rem" => Ok(Length::Rem(value))
"in" => Ok(Length::Absolute(Au((value * AU_PER_IN) as i32))),
"cm" => Ok(Length::Absolute(Au((value * AU_PER_CM) as i32))),
"mm" => Ok(Length::Absolute(Au((value * AU_PER_MM) as i32))),
"pt" => Ok(Length::Absolute(Au((value * AU_PER_PT) as i32))),
"pc" => Ok(Length::Absolute(Au((value * AU_PER_PC) as i32))),
// font-relative
"em" => Ok(Length::FontRelative(FontRelativeLength::Em(value))),
"ex" => Ok(Length::FontRelative(FontRelativeLength::Ex(value))),
"ch" => Err(()),
"rem" => Ok(Length::FontRelative(FontRelativeLength::Rem(value))),
_ => Err(())
}
}
#[inline]
pub fn from_px(px_value: CSSFloat) -> Length {
Length::Au(Au((px_value * AU_PER_PX) as i32))
Length::Absolute(Au((px_value * AU_PER_PX) as i32))
}
}
@ -245,7 +273,7 @@ pub mod specified {
Ok(LengthOrPercentage::Percentage(value.unit_value))
}
Token::Number(ref value) if value.value == 0. => {
Ok(LengthOrPercentage::Length(Length::Au(Au(0))))
Ok(LengthOrPercentage::Length(Length::Absolute(Au(0))))
}
_ => Err(())
}
@ -294,7 +322,7 @@ pub mod specified {
Ok(LengthOrPercentageOrAuto::Percentage(value.unit_value))
}
Token::Number(ref value) if value.value == 0. => {
Ok(LengthOrPercentageOrAuto::Length(Length::Au(Au(0))))
Ok(LengthOrPercentageOrAuto::Length(Length::Absolute(Au(0))))
}
Token::Ident(ref value) if value.eq_ignore_ascii_case("auto") => {
Ok(LengthOrPercentageOrAuto::Auto)
@ -345,7 +373,7 @@ pub mod specified {
Ok(LengthOrPercentageOrNone::Percentage(value.unit_value))
}
Token::Number(ref value) if value.value == 0. => {
Ok(LengthOrPercentageOrNone::Length(Length::Au(Au(0))))
Ok(LengthOrPercentageOrNone::Length(Length::Absolute(Au(0))))
}
Token::Ident(ref value) if value.eq_ignore_ascii_case("none") => {
Ok(LengthOrPercentageOrNone::None)
@ -386,7 +414,7 @@ pub mod specified {
Ok(PositionComponent::Percentage(value.unit_value))
}
Token::Number(ref value) if value.value == 0. => {
Ok(PositionComponent::Length(Length::Au(Au(0))))
Ok(PositionComponent::Length(Length::Absolute(Au(0))))
}
Token::Ident(value) => {
match_ignore_ascii_case! { value,
@ -736,7 +764,13 @@ pub mod computed {
#[inline]
fn to_computed_value(&self, context: &Context) -> Au {
self.to_computed_value_with_font_size(context.font_size, context.root_font_size)
match self {
&specified::Length::Absolute(length) => length,
&specified::Length::FontRelative(length) =>
length.to_computed_value(context.font_size, context.root_font_size),
&specified::Length::ServoCharacterWidth(length) =>
length.to_computed_value(context.font_size)
}
}
}