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