Bug 1396692 - Part 1: Add a method for FontRelativeLength to return pixel value. draft
authorBoris Chiou <boris.chiou@gmail.com>
Fri, 01 Sep 2017 18:07:10 +0800
changeset 659737 9ceebccfe19974e731f3b964177fedb0fb6f298a
parent 658813 1401e3eec44df87963d3af329ef8a4183ab0483f
child 659738 4a675c32eaa5af178ac731669b49d5cf63dd85d0
child 659757 e79d01326057d00ffd606a440820ee8e599fbb28
push id78203
push userbmo:boris.chiou@gmail.com
push dateWed, 06 Sep 2017 08:58:36 +0000
bugs1396692
milestone57.0a1
Bug 1396692 - Part 1: Add a method for FontRelativeLength to return pixel value. MozReview-Commit-ID: Hdt8KK6E4Kp
servo/components/style/values/specified/length.rs
--- a/servo/components/style/values/specified/length.rs
+++ b/servo/components/style/values/specified/length.rs
@@ -96,78 +96,122 @@ impl FontBaseSize {
         match *self {
             FontBaseSize::Custom(size) => size,
             FontBaseSize::CurrentStyle => context.style().get_font().clone_font_size().0,
             FontBaseSize::InheritedStyle => context.style().get_parent_font().clone_font_size().0,
         }
     }
 }
 
+/// Font reference size type.
+#[derive(Clone, Copy, Debug, PartialEq)]
+enum FontReferenceSize {
+    /// Represented as the normal font referent size.
+    Full(Au),
+    /// Represented as the case in which we cannot determine the size, so assumes 0.5em.
+    Half(Au),
+}
+
 impl FontRelativeLength {
-    /// Computes the font-relative length. We use the base_size
-    /// flag to pass a different size for computing font-size and unconstrained font-size
+    /// Computes the font-relative length.
     pub fn to_computed_value(&self, context: &Context, base_size: FontBaseSize) -> Au {
+        let (reference_size, length) = self.reference_font_size_and_length(context, base_size);
+        match reference_size {
+            FontReferenceSize::Full(au) => au.scale_by(length),
+            FontReferenceSize::Half(au) => au.scale_by(0.5 * length),
+        }
+    }
+
+    /// Computes the font-relative length, but as pixel value.
+    pub fn to_px(&self, context: &Context, base_size: FontBaseSize) -> CSSFloat {
+        use std::f32;
+        let (reference_size, length) = self.reference_font_size_and_length(context, base_size);
+        let pixel = match reference_size {
+            FontReferenceSize::Full(size) => length * size.to_f32_px(),
+            FontReferenceSize::Half(size) => length * 0.5 * size.to_f32_px(),
+        };
+        pixel.min(f32::MAX).max(f32::MIN)
+    }
+
+    /// Return reference font size. We use the base_size flag to pass a different size
+    /// for computing font-size and unconstrained font-size.
+    fn reference_font_size_and_length(
+        &self,
+        context: &Context,
+        base_size: FontBaseSize,
+    ) -> (FontReferenceSize, CSSFloat) {
         fn query_font_metrics(context: &Context, reference_font_size: Au) -> FontMetricsQueryResult {
             context.font_metrics_provider.query(context.style().get_font(),
                                                 reference_font_size,
                                                 context.style().writing_mode,
                                                 context.in_media_query,
                                                 context.device())
         }
 
         let reference_font_size = base_size.resolve(context);
 
         match *self {
-            FontRelativeLength::Em(length) => reference_font_size.scale_by(length),
+            FontRelativeLength::Em(length) => {
+                (FontReferenceSize::Full(reference_font_size), length)
+            },
             FontRelativeLength::Ex(length) => {
-                match query_font_metrics(context, reference_font_size) {
-                    FontMetricsQueryResult::Available(metrics) => metrics.x_height.scale_by(length),
+                let reference_size = match query_font_metrics(context, reference_font_size) {
+                    FontMetricsQueryResult::Available(metrics) => {
+                        FontReferenceSize::Full(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.
                     //
-                    FontMetricsQueryResult::NotAvailable => reference_font_size.scale_by(0.5 * length),
-                }
+                    FontMetricsQueryResult::NotAvailable => {
+                        FontReferenceSize::Half(reference_font_size)
+                    },
+                };
+                (reference_size, length)
             },
             FontRelativeLength::Ch(length) => {
-                match query_font_metrics(context, reference_font_size) {
-                    FontMetricsQueryResult::Available(metrics) => metrics.zero_advance_measure.scale_by(length),
+                let reference_size = match query_font_metrics(context, reference_font_size) {
+                    FontMetricsQueryResult::Available(metrics) => {
+                        FontReferenceSize::Full(metrics.zero_advance_measure)
+                    },
                     // 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).
                     //
                     FontMetricsQueryResult::NotAvailable => {
                         if context.style().writing_mode.is_vertical() {
-                            reference_font_size.scale_by(length)
+                            FontReferenceSize::Full(reference_font_size)
                         } else {
-                            reference_font_size.scale_by(0.5 * length)
+                            FontReferenceSize::Half(reference_font_size)
                         }
                     }
-                }
+                };
+                (reference_size, length)
             }
             FontRelativeLength::Rem(length) => {
                 // https://drafts.csswg.org/css-values/#rem:
                 //
                 //     When specified on the font-size property of the root
                 //     element, the rem units refer to the property’s initial
                 //     value.
                 //
-                if context.is_root_element {
-                    reference_font_size.scale_by(length)
+                let reference_size = if context.is_root_element {
+                    FontReferenceSize::Full(reference_font_size)
                 } else {
-                    context.device().root_font_size().scale_by(length)
-                }
+                    FontReferenceSize::Full(context.device().root_font_size())
+                };
+                (reference_size, length)
             }
         }
     }
 }
 
 #[derive(Clone, Copy, Debug, PartialEq)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 /// A viewport-relative length.