style/values/specified/
resolution.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5//! Resolution values:
6//!
7//! https://drafts.csswg.org/css-values/#resolution
8
9use crate::derives::*;
10use crate::parser::{Parse, ParserContext};
11use crate::values::specified::CalcNode;
12use crate::values::CSSFloat;
13use cssparser::{match_ignore_ascii_case, Parser, Token};
14use std::fmt::{self, Write};
15use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
16
17/// A specified resolution.
18#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToShmem)]
19pub struct Resolution {
20    value: CSSFloat,
21    unit: ResolutionUnit,
22    was_calc: bool,
23}
24
25#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
26enum ResolutionUnit {
27    /// Dots per inch.
28    Dpi,
29    /// An alias unit for dots per pixel.
30    X,
31    /// Dots per pixel.
32    Dppx,
33    /// Dots per centimeter.
34    Dpcm,
35}
36
37impl ResolutionUnit {
38    fn as_str(self) -> &'static str {
39        match self {
40            Self::Dpi => "dpi",
41            Self::X => "x",
42            Self::Dppx => "dppx",
43            Self::Dpcm => "dpcm",
44        }
45    }
46}
47
48impl Resolution {
49    /// Returns a resolution value from dppx units.
50    pub fn from_dppx(value: CSSFloat) -> Self {
51        Self {
52            value,
53            unit: ResolutionUnit::Dppx,
54            was_calc: false,
55        }
56    }
57
58    /// Returns a resolution value from dppx units.
59    pub fn from_x(value: CSSFloat) -> Self {
60        Self {
61            value,
62            unit: ResolutionUnit::X,
63            was_calc: false,
64        }
65    }
66
67    /// Returns a resolution value from dppx units.
68    pub fn from_dppx_calc(value: CSSFloat) -> Self {
69        Self {
70            value,
71            unit: ResolutionUnit::Dppx,
72            was_calc: true,
73        }
74    }
75
76    /// Convert this resolution value to dppx units.
77    pub fn dppx(&self) -> CSSFloat {
78        match self.unit {
79            ResolutionUnit::X | ResolutionUnit::Dppx => self.value,
80            _ => self.dpi() / 96.0,
81        }
82    }
83
84    /// Convert this resolution value to dpi units.
85    pub fn dpi(&self) -> CSSFloat {
86        match self.unit {
87            ResolutionUnit::Dpi => self.value,
88            ResolutionUnit::X | ResolutionUnit::Dppx => self.value * 96.0,
89            ResolutionUnit::Dpcm => self.value * 2.54,
90        }
91    }
92
93    /// Parse a resolution given a value and unit.
94    pub fn parse_dimension<'i, 't>(value: CSSFloat, unit: &str) -> Result<Self, ()> {
95        let unit = match_ignore_ascii_case! { &unit,
96            "dpi" => ResolutionUnit::Dpi,
97            "dppx" => ResolutionUnit::Dppx,
98            "dpcm" => ResolutionUnit::Dpcm,
99            "x" => ResolutionUnit::X,
100            _ => return Err(())
101        };
102        Ok(Self {
103            value,
104            unit,
105            was_calc: false,
106        })
107    }
108}
109
110impl ToCss for Resolution {
111    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
112    where
113        W: Write,
114    {
115        crate::values::serialize_specified_dimension(
116            self.value,
117            self.unit.as_str(),
118            self.was_calc,
119            dest,
120        )
121    }
122}
123
124impl Parse for Resolution {
125    fn parse<'i, 't>(
126        context: &ParserContext,
127        input: &mut Parser<'i, 't>,
128    ) -> Result<Self, ParseError<'i>> {
129        let location = input.current_source_location();
130        match *input.next()? {
131            Token::Dimension {
132                value, ref unit, ..
133            } if value >= 0. => Self::parse_dimension(value, unit)
134                .map_err(|()| location.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
135            Token::Function(ref name) => {
136                let function = CalcNode::math_function(context, name, location)?;
137                CalcNode::parse_resolution(context, input, function)
138            },
139            ref t => return Err(location.new_unexpected_token_error(t.clone())),
140        }
141    }
142}