1use crate::parser::{Parse, ParserContext};
8use crate::values::animated::ToAnimatedZero;
9use crate::{One, Zero};
10use byteorder::{BigEndian, ReadBytesExt};
11use cssparser::Parser;
12use std::fmt::{self, Write};
13use std::io::Cursor;
14use style_traits::{CssWriter, ParseError};
15use style_traits::{StyleParseErrorKind, ToCss};
16
17pub trait TaggedFontValue {
20    fn tag(&self) -> FontTag;
22}
23
24#[derive(
26    Clone,
27    Debug,
28    Eq,
29    MallocSizeOf,
30    PartialEq,
31    SpecifiedValueInfo,
32    ToAnimatedValue,
33    ToComputedValue,
34    ToResolvedValue,
35    ToShmem,
36)]
37pub struct FeatureTagValue<Integer> {
38    pub tag: FontTag,
40    pub value: Integer,
42}
43
44impl<T> TaggedFontValue for FeatureTagValue<T> {
45    fn tag(&self) -> FontTag {
46        self.tag
47    }
48}
49
50impl<Integer> ToCss for FeatureTagValue<Integer>
51where
52    Integer: One + ToCss + PartialEq,
53{
54    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
55    where
56        W: Write,
57    {
58        self.tag.to_css(dest)?;
59        if !self.value.is_one() {
61            dest.write_char(' ')?;
62            self.value.to_css(dest)?;
63        }
64
65        Ok(())
66    }
67}
68
69#[derive(
73    Animate,
74    Clone,
75    ComputeSquaredDistance,
76    Debug,
77    Eq,
78    MallocSizeOf,
79    PartialEq,
80    SpecifiedValueInfo,
81    ToAnimatedValue,
82    ToComputedValue,
83    ToCss,
84    ToResolvedValue,
85    ToShmem,
86)]
87#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
88pub struct VariationValue<Number> {
89    #[animation(constant)]
91    pub tag: FontTag,
92    pub value: Number,
94}
95
96impl<T> TaggedFontValue for VariationValue<T> {
97    fn tag(&self) -> FontTag {
98        self.tag
99    }
100}
101
102#[derive(
104    Clone,
105    Debug,
106    Eq,
107    MallocSizeOf,
108    PartialEq,
109    SpecifiedValueInfo,
110    ToAnimatedValue,
111    ToCss,
112    ToResolvedValue,
113    ToShmem,
114    ToTyped,
115)]
116#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
117#[css(comma)]
118pub struct FontSettings<T>(#[css(if_empty = "normal", iterable)] pub Box<[T]>);
119
120impl<T> FontSettings<T> {
121    #[inline]
123    pub fn normal() -> Self {
124        FontSettings(vec![].into_boxed_slice())
125    }
126}
127
128impl<T: Parse> Parse for FontSettings<T> {
129    fn parse<'i, 't>(
132        context: &ParserContext,
133        input: &mut Parser<'i, 't>,
134    ) -> Result<Self, ParseError<'i>> {
135        if input
136            .try_parse(|i| i.expect_ident_matching("normal"))
137            .is_ok()
138        {
139            return Ok(Self::normal());
140        }
141
142        Ok(FontSettings(
143            input
144                .parse_comma_separated(|i| T::parse(context, i))?
145                .into_boxed_slice(),
146        ))
147    }
148}
149
150#[derive(
157    Clone,
158    Copy,
159    Debug,
160    Eq,
161    MallocSizeOf,
162    PartialEq,
163    SpecifiedValueInfo,
164    ToAnimatedValue,
165    ToComputedValue,
166    ToResolvedValue,
167    ToShmem,
168)]
169#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
170pub struct FontTag(pub u32);
171
172impl ToCss for FontTag {
173    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
174    where
175        W: Write,
176    {
177        use byteorder::ByteOrder;
178        use std::str;
179
180        let mut raw = [0u8; 4];
181        BigEndian::write_u32(&mut raw, self.0);
182        str::from_utf8(&raw).unwrap_or_default().to_css(dest)
183    }
184}
185
186impl Parse for FontTag {
187    fn parse<'i, 't>(
188        _context: &ParserContext,
189        input: &mut Parser<'i, 't>,
190    ) -> Result<Self, ParseError<'i>> {
191        let location = input.current_source_location();
192        let tag = input.expect_string()?;
193
194        if tag.len() != 4 || tag.as_bytes().iter().any(|c| *c < b' ' || *c > b'~') {
196            return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
197        }
198
199        let mut raw = Cursor::new(tag.as_bytes());
200        Ok(FontTag(raw.read_u32::<BigEndian>().unwrap()))
201    }
202}
203
204#[allow(missing_docs)]
208#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
209#[derive(
210    Animate,
211    Clone,
212    ComputeSquaredDistance,
213    Copy,
214    Debug,
215    Hash,
216    MallocSizeOf,
217    PartialEq,
218    SpecifiedValueInfo,
219    ToAnimatedValue,
220    ToAnimatedZero,
221    ToResolvedValue,
222    ToShmem,
223)]
224#[value_info(other_values = "normal")]
225pub enum FontStyle<Angle> {
226    #[value_info(starts_with_keyword)]
228    Oblique(Angle),
229    #[animation(error)]
230    Italic,
231}
232
233impl<Angle: Zero> FontStyle<Angle> {
234    pub fn normal() -> Self {
236        Self::Oblique(Angle::zero())
237    }
238}
239
240#[allow(missing_docs)]
244#[repr(u8)]
245#[derive(
246    Animate,
247    Clone,
248    ComputeSquaredDistance,
249    Copy,
250    Debug,
251    Hash,
252    MallocSizeOf,
253    PartialEq,
254    SpecifiedValueInfo,
255    ToAnimatedValue,
256    ToAnimatedZero,
257    ToComputedValue,
258    ToResolvedValue,
259    ToShmem,
260    ToTyped,
261)]
262pub enum GenericFontSizeAdjust<Factor> {
263    #[animation(error)]
264    None,
265    #[value_info(starts_with_keyword)]
266    ExHeight(Factor),
267    #[value_info(starts_with_keyword)]
268    CapHeight(Factor),
269    #[value_info(starts_with_keyword)]
270    ChWidth(Factor),
271    #[value_info(starts_with_keyword)]
272    IcWidth(Factor),
273    #[value_info(starts_with_keyword)]
274    IcHeight(Factor),
275}
276
277impl<Factor: ToCss> ToCss for GenericFontSizeAdjust<Factor> {
278    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
279    where
280        W: Write,
281    {
282        let (prefix, value) = match self {
283            Self::None => return dest.write_str("none"),
284            Self::ExHeight(v) => ("", v),
285            Self::CapHeight(v) => ("cap-height ", v),
286            Self::ChWidth(v) => ("ch-width ", v),
287            Self::IcWidth(v) => ("ic-width ", v),
288            Self::IcHeight(v) => ("ic-height ", v),
289        };
290
291        dest.write_str(prefix)?;
292        value.to_css(dest)
293    }
294}
295
296#[derive(
298    Animate,
299    Clone,
300    ComputeSquaredDistance,
301    Copy,
302    Debug,
303    MallocSizeOf,
304    PartialEq,
305    SpecifiedValueInfo,
306    ToAnimatedValue,
307    ToCss,
308    ToShmem,
309    Parse,
310    ToTyped,
311)]
312#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
313#[repr(C, u8)]
314pub enum GenericLineHeight<N, L> {
315    Normal,
317    #[cfg(feature = "gecko")]
319    #[parse(condition = "ParserContext::in_ua_sheet")]
320    MozBlockHeight,
321    Number(N),
323    Length(L),
325}
326
327pub use self::GenericLineHeight as LineHeight;
328
329impl<N, L> ToAnimatedZero for LineHeight<N, L> {
330    #[inline]
331    fn to_animated_zero(&self) -> Result<Self, ()> {
332        Err(())
333    }
334}
335
336impl<N, L> LineHeight<N, L> {
337    #[inline]
339    pub fn normal() -> Self {
340        LineHeight::Normal
341    }
342}