style: Refactor and add infrastructure for font metrics in style.

This commit itself only moves things around and adds an extra parameter to the
`apply_declarations` function to eventually handle #14079 correctly.

Probably needs a more granular API to query fonts, á la nsFontMetrics, but
that's trivial to do once this is landed.

Then we should make the font provider mandatory, and implement the missing stylo
bits.
This commit is contained in:
Emilio Cobos Álvarez 2016-11-11 19:56:04 +01:00
parent 9fd6f0acd5
commit 6c3458767b
No known key found for this signature in database
GPG key ID: 056B727BB9C1027C
15 changed files with 196 additions and 94 deletions

View file

@ -17,14 +17,14 @@ pub use values::specified::{Angle, BorderStyle, Time, UrlOrNone};
#[derive(Clone, PartialEq, Copy, Debug)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct CalcLengthOrPercentage {
pub length: Option<Au>,
pub length: Au,
pub percentage: Option<CSSFloat>,
}
impl CalcLengthOrPercentage {
#[inline]
pub fn length(&self) -> Au {
self.length.unwrap_or(Au(0))
self.length
}
#[inline]
@ -38,13 +38,13 @@ impl From<LengthOrPercentage> for CalcLengthOrPercentage {
match len {
LengthOrPercentage::Percentage(this) => {
CalcLengthOrPercentage {
length: None,
length: Au(0),
percentage: Some(this),
}
}
LengthOrPercentage::Length(this) => {
CalcLengthOrPercentage {
length: Some(this),
length: this,
percentage: None,
}
}
@ -60,13 +60,13 @@ impl From<LengthOrPercentageOrAuto> for Option<CalcLengthOrPercentage> {
match len {
LengthOrPercentageOrAuto::Percentage(this) => {
Some(CalcLengthOrPercentage {
length: None,
length: Au(0),
percentage: Some(this),
})
}
LengthOrPercentageOrAuto::Length(this) => {
Some(CalcLengthOrPercentage {
length: Some(this),
length: this,
percentage: None,
})
}
@ -83,10 +83,9 @@ impl From<LengthOrPercentageOrAuto> for Option<CalcLengthOrPercentage> {
impl ToCss for CalcLengthOrPercentage {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
match (self.length, self.percentage) {
(None, Some(p)) => write!(dest, "{}%", p * 100.),
(Some(l), None) => write!(dest, "{}px", Au::to_px(l)),
(Some(l), Some(p)) => write!(dest, "calc({}px + {}%)", Au::to_px(l), p * 100.),
_ => unreachable!()
(l, Some(p)) if l == Au(0) => write!(dest, "{}%", p * 100.),
(l, Some(p)) => write!(dest, "calc({}px + {}%)", Au::to_px(l), p * 100.),
(l, None) => write!(dest, "{}px", Au::to_px(l)),
}
}
}
@ -95,16 +94,34 @@ impl ToComputedValue for specified::CalcLengthOrPercentage {
type ComputedValue = CalcLengthOrPercentage;
fn to_computed_value(&self, context: &Context) -> CalcLengthOrPercentage {
self.compute_from_viewport_and_font_size(context.viewport_size(),
context.style().get_font().clone_font_size(),
context.style().root_font_size())
let mut length = Au(0);
if let Some(absolute) = self.absolute {
length += absolute;
}
for val in &[self.vw, self.vh, self.vmin, self.vmax] {
if let Some(val) = *val {
length += val.to_computed_value(context.viewport_size());
}
}
for val in &[self.ch, self.em, self.ex, self.rem] {
if let Some(val) = *val {
length += val.to_computed_value(context, /* use inherited */ false);
}
}
CalcLengthOrPercentage {
length: length,
percentage: self.percentage.map(|p| p.0),
}
}
#[inline]
fn from_computed_value(computed: &CalcLengthOrPercentage) -> Self {
specified::CalcLengthOrPercentage {
absolute: computed.length,
absolute: Some(computed.length),
percentage: computed.percentage.map(specified::Percentage),
..Default::default()
}

View file

@ -4,6 +4,7 @@
use app_units::Au;
use euclid::size::Size2D;
use font_metrics::FontMetricsProvider;
use properties::ComputedValues;
use std::fmt;
use style_traits::ToCss;
@ -28,9 +29,11 @@ pub struct Context<'a> {
pub viewport_size: Size2D<Au>,
pub inherited_style: &'a ComputedValues,
/// Values access through this need to be in the properties "computed early":
/// color, text-decoration, font-size, display, position, float, border-*-style, outline-style
/// Values access through this need to be in the properties "computed
/// early": color, text-decoration, font-size, display, position, float,
/// border-*-style, outline-style, font-family, writing-mode...
pub style: ComputedValues,
pub font_metrics_provider: Option<&'a FontMetricsProvider>,
}
impl<'a> Context<'a> {
@ -39,6 +42,20 @@ impl<'a> Context<'a> {
pub fn inherited_style(&self) -> &ComputedValues { &self.inherited_style }
pub fn style(&self) -> &ComputedValues { &self.style }
pub fn mutate_style(&mut self) -> &mut ComputedValues { &mut self.style }
/// Creates a dummy computed context for use in multiple places, like
/// evaluating media queries.
pub fn initial(viewport_size: Size2D<Au>, is_root_element: bool) -> Self {
let initial_style = ComputedValues::initial_values();
// FIXME: Enforce a font metrics provider.
Context {
is_root_element: is_root_element,
viewport_size: viewport_size,
inherited_style: initial_style,
style: initial_style.clone(),
font_metrics_provider: None,
}
}
}
pub trait ToComputedValue {
@ -99,8 +116,7 @@ impl ToComputedValue for specified::Length {
specified::Length::Absolute(length) => length,
specified::Length::Calc(calc, range) => range.clamp(calc.to_computed_value(context).length()),
specified::Length::FontRelative(length) =>
length.to_computed_value(context.style().get_font().clone_font_size(),
context.style().root_font_size()),
length.to_computed_value(context, /* use inherited */ false),
specified::Length::ViewportPercentage(length) =>
length.to_computed_value(context.viewport_size()),
specified::Length::ServoCharacterWidth(length) =>

View file

@ -5,6 +5,7 @@
use app_units::Au;
use cssparser::{Parser, Token};
use euclid::size::Size2D;
use font_metrics::FontMetrics;
use parser::Parse;
use std::ascii::AsciiExt;
use std::cmp;
@ -13,7 +14,8 @@ use std::ops::Mul;
use style_traits::ToCss;
use style_traits::values::specified::AllowedNumericType;
use super::{Angle, Number, SimplifiedValueNode, SimplifiedSumNode, Time};
use values::{CSSFloat, Either, FONT_MEDIUM_PX, HasViewportPercentage, None_, computed};
use values::{CSSFloat, Either, FONT_MEDIUM_PX, HasViewportPercentage, None_};
use values::computed::Context;
pub use super::image::{AngleOrCorner, ColorStop, EndingShape as GradientEndingShape, Gradient};
pub use super::image::{GradientKind, HorizontalDirection, Image, LengthOrKeyword, LengthOrPercentageOrKeyword};
@ -40,18 +42,78 @@ impl ToCss for FontRelativeLength {
}
impl FontRelativeLength {
pub fn to_computed_value(&self,
reference_font_size: Au,
root_font_size: Au)
-> Au
{
pub fn find_first_available_font_metrics(context: &Context) -> Option<FontMetrics> {
use font_metrics::FontMetricsQueryResult::*;
if let Some(ref metrics_provider) = context.font_metrics_provider {
for family in context.style().get_font().font_family_iter() {
if let Available(metrics) = metrics_provider.query(family.atom()) {
return metrics;
}
}
}
None
}
// NB: The use_inherited flag is used to special-case the computation of
// font-family.
pub fn to_computed_value(&self, context: &Context, use_inherited: bool) -> Au {
let reference_font_size = if use_inherited {
context.inherited_style().get_font().clone_font_size()
} else {
context.style().get_font().clone_font_size()
};
let root_font_size = context.style().root_font_size;
match *self {
FontRelativeLength::Em(length) => reference_font_size.scale_by(length),
FontRelativeLength::Ex(length) | FontRelativeLength::Ch(length) => {
// https://github.com/servo/servo/issues/7462
let em_factor = 0.5;
reference_font_size.scale_by(length * em_factor)
FontRelativeLength::Ex(length) => {
match Self::find_first_available_font_metrics(context) {
Some(metrics) => metrics.x_height,
// https://drafts.csswg.org/css-values/#ex
//
// In the cases where it is impossible or impractical to
// determine the x-height, a value of 0.5em must be
// assumed.
//
None => reference_font_size.scale_by(0.5 * length),
}
},
FontRelativeLength::Ch(length) => {
let wm = context.style().writing_mode;
// TODO(emilio, #14144): Compute this properly once we support
// all the relevant writing-mode related properties, this should
// be equivalent to "is the text in the block direction?".
let vertical = wm.is_vertical();
match Self::find_first_available_font_metrics(context) {
Some(metrics) => {
if vertical {
metrics.zero_advance_measure.height
} else {
metrics.zero_advance_measure.width
}
}
// https://drafts.csswg.org/css-values/#ch
//
// In the cases where it is impossible or impractical to
// determine the measure of the “0” glyph, it must be
// assumed to be 0.5em wide by 1em tall. Thus, the ch
// unit falls back to 0.5em in the general case, and to
// 1em when it would be typeset upright (i.e.
// writing-mode is vertical-rl or vertical-lr and
// text-orientation is upright).
//
None => {
if vertical {
reference_font_size.scale_by(length)
} else {
reference_font_size.scale_by(0.5 * length)
}
}
}
}
FontRelativeLength::Rem(length) => root_font_size.scale_by(length)
}
}
@ -612,38 +674,6 @@ impl CalcLengthOrPercentage {
_ => Err(())
}
}
pub fn compute_from_viewport_and_font_size(&self,
viewport_size: Size2D<Au>,
font_size: Au,
root_font_size: Au)
-> computed::CalcLengthOrPercentage
{
let mut length = None;
if let Some(absolute) = self.absolute {
length = Some(length.unwrap_or(Au(0)) + absolute);
}
for val in &[self.vw, self.vh, self.vmin, self.vmax] {
if let Some(val) = *val {
length = Some(length.unwrap_or(Au(0)) +
val.to_computed_value(viewport_size));
}
}
for val in &[self.ch, self.em, self.ex, self.rem] {
if let Some(val) = *val {
length = Some(length.unwrap_or(Au(0)) + val.to_computed_value(
font_size, root_font_size));
}
}
computed::CalcLengthOrPercentage {
length: length,
percentage: self.percentage.map(|p| p.0),
}
}
}
impl HasViewportPercentage for CalcLengthOrPercentage {

View file

@ -7,6 +7,7 @@
//!
//! [position]: https://drafts.csswg.org/css-backgrounds-3/#position
use app_units::Au;
use cssparser::{Parser, Token};
use parser::Parse;
use std::fmt;
@ -290,9 +291,9 @@ impl ToComputedValue for Position {
Keyword::Right => {
if let Some(x) = self.horiz_position {
let (length, percentage) = match x {
LengthOrPercentage::Percentage(Percentage(y)) => (None, Some(1.0 - y)),
LengthOrPercentage::Length(y) => (Some(-y.to_computed_value(context)), Some(1.0)),
_ => (None, None),
LengthOrPercentage::Percentage(Percentage(y)) => (Au(0), Some(1.0 - y)),
LengthOrPercentage::Length(y) => (-y.to_computed_value(context), Some(1.0)),
_ => (Au(0), None),
};
ComputedLengthOrPercentage::Calc(CalcLengthOrPercentage {
length: length,
@ -316,9 +317,9 @@ impl ToComputedValue for Position {
Keyword::Bottom => {
if let Some(x) = self.vert_position {
let (length, percentage) = match x {
LengthOrPercentage::Percentage(Percentage(y)) => (None, Some(1.0 - y)),
LengthOrPercentage::Length(y) => (Some(-y.to_computed_value(context)), Some(1.0)),
_ => (None, None),
LengthOrPercentage::Percentage(Percentage(y)) => (Au(0), Some(1.0 - y)),
LengthOrPercentage::Length(y) => (-y.to_computed_value(context), Some(1.0)),
_ => (Au(0), None),
};
ComputedLengthOrPercentage::Calc(CalcLengthOrPercentage {
length: length,