Skip to main content

style/values/generics/
font.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//! Generic types for font stuff.
6
7use crate::derives::*;
8use crate::parser::{Parse, ParserContext};
9use crate::typed_om::{KeywordValue, ToTyped, TypedValue};
10use crate::values::animated::ToAnimatedZero;
11use crate::{One, Zero};
12use byteorder::{BigEndian, ReadBytesExt};
13use cssparser::Parser;
14use std::fmt::{self, Write};
15use std::io::Cursor;
16use style_traits::{CssString, CssWriter, ParseError, StyleParseErrorKind, ToCss};
17use thin_vec::ThinVec;
18
19/// A trait for values that are labelled with a FontTag (for feature and
20/// variation settings).
21pub trait TaggedFontValue {
22    /// The value's tag.
23    fn tag(&self) -> FontTag;
24}
25
26/// https://drafts.csswg.org/css-fonts-4/#feature-tag-value
27#[derive(
28    Clone,
29    Debug,
30    Deserialize,
31    Eq,
32    Hash,
33    MallocSizeOf,
34    PartialEq,
35    Serialize,
36    SpecifiedValueInfo,
37    ToAnimatedValue,
38    ToComputedValue,
39    ToResolvedValue,
40    ToShmem,
41)]
42#[repr(C)]
43pub struct FeatureTagValue<Integer> {
44    /// A four-character tag, packed into a u32 (one byte per character).
45    pub tag: FontTag,
46    /// The actual value.
47    pub value: Integer,
48}
49
50impl<T> TaggedFontValue for FeatureTagValue<T> {
51    fn tag(&self) -> FontTag {
52        self.tag
53    }
54}
55
56impl<Integer> ToCss for FeatureTagValue<Integer>
57where
58    Integer: One + ToCss + PartialEq,
59{
60    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
61    where
62        W: Write,
63    {
64        self.tag.to_css(dest)?;
65        // Don't serialize the default value.
66        if !self.value.is_one() {
67            dest.write_char(' ')?;
68            self.value.to_css(dest)?;
69        }
70
71        Ok(())
72    }
73}
74
75/// Variation setting for a single feature, see:
76///
77/// https://drafts.csswg.org/css-fonts-4/#font-variation-settings-def
78#[derive(
79    Animate,
80    Clone,
81    ComputeSquaredDistance,
82    Debug,
83    Deserialize,
84    Eq,
85    MallocSizeOf,
86    PartialEq,
87    Serialize,
88    SpecifiedValueInfo,
89    ToAnimatedValue,
90    ToComputedValue,
91    ToCss,
92    ToResolvedValue,
93    ToShmem,
94)]
95#[repr(C)]
96pub struct VariationValue<Number> {
97    /// A four-character tag, packed into a u32 (one byte per character).
98    #[animation(constant)]
99    pub tag: FontTag,
100    /// The actual value.
101    pub value: Number,
102}
103
104impl<T> TaggedFontValue for VariationValue<T> {
105    fn tag(&self) -> FontTag {
106        self.tag
107    }
108}
109
110/// A value both for font-variation-settings and font-feature-settings.
111#[derive(
112    Clone,
113    Debug,
114    Deserialize,
115    Eq,
116    Hash,
117    MallocSizeOf,
118    PartialEq,
119    Serialize,
120    SpecifiedValueInfo,
121    ToAnimatedValue,
122    ToCss,
123    ToResolvedValue,
124    ToShmem,
125    ToTyped,
126)]
127#[css(comma)]
128#[repr(transparent)]
129#[typed(todo_derive_fields)]
130pub struct FontSettings<T>(#[css(if_empty = "normal", iterable)] pub ThinVec<T>);
131
132impl<T> FontSettings<T> {
133    /// Default value of font settings as `normal`.
134    #[inline]
135    pub fn normal() -> Self {
136        FontSettings(Default::default())
137    }
138}
139
140impl<T: Parse> Parse for FontSettings<T> {
141    /// https://drafts.csswg.org/css-fonts-4/#descdef-font-face-font-feature-settings
142    /// https://drafts.csswg.org/css-fonts-4/#font-variation-settings-def
143    fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
144        if input
145            .try_parse(|i| i.expect_ident_matching("normal"))
146            .is_ok()
147        {
148            return Ok(Self::normal());
149        }
150
151        Ok(FontSettings(
152            input
153                .parse_comma_separated(|i| T::parse(context, i))?
154                .into(),
155        ))
156    }
157}
158
159/// A font four-character tag, represented as a u32 for convenience.
160///
161/// See:
162///   https://drafts.csswg.org/css-fonts-4/#font-variation-settings-def
163///   https://drafts.csswg.org/css-fonts-4/#descdef-font-face-font-feature-settings
164#[derive(
165    Clone,
166    Copy,
167    Deserialize,
168    Eq,
169    Hash,
170    MallocSizeOf,
171    PartialEq,
172    Serialize,
173    SpecifiedValueInfo,
174    ToAnimatedValue,
175    ToComputedValue,
176    ToResolvedValue,
177    ToShmem,
178)]
179#[repr(transparent)]
180pub struct FontTag(pub u32);
181
182impl fmt::Debug for FontTag {
183    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
184        let tag_bytes = self.0.to_be_bytes();
185
186        let mut tuple = f.debug_tuple("FontTag");
187        if let Ok(utf8_tag) = str::from_utf8(&tag_bytes) {
188            tuple.field(&utf8_tag);
189        } else {
190            tuple.field(&tag_bytes);
191        };
192        tuple.finish()
193    }
194}
195
196impl ToCss for FontTag {
197    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
198    where
199        W: Write,
200    {
201        use byteorder::ByteOrder;
202        use std::str;
203
204        let mut raw = [0u8; 4];
205        BigEndian::write_u32(&mut raw, self.0);
206        str::from_utf8(&raw).unwrap_or_default().to_css(dest)
207    }
208}
209
210impl Parse for FontTag {
211    fn parse(_context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
212        let tag = input.expect_string()?;
213
214        // allowed strings of length 4 containing chars: <U+20, U+7E>
215        if tag.len() != 4 || tag.as_bytes().iter().any(|c| *c < b' ' || *c > b'~') {
216            return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError));
217        }
218
219        let mut raw = Cursor::new(tag.as_bytes());
220        Ok(FontTag(raw.read_u32::<BigEndian>().unwrap()))
221    }
222}
223
224/// A generic value for the `font-style` property.
225///
226/// https://drafts.csswg.org/css-fonts-4/#font-style-prop
227#[allow(missing_docs)]
228#[derive(
229    Animate,
230    Clone,
231    ComputeSquaredDistance,
232    Copy,
233    Debug,
234    Deserialize,
235    Hash,
236    MallocSizeOf,
237    PartialEq,
238    Serialize,
239    SpecifiedValueInfo,
240    ToAnimatedValue,
241    ToAnimatedZero,
242    ToResolvedValue,
243    ToShmem,
244)]
245#[value_info(other_values = "normal")]
246pub enum FontStyle<Angle> {
247    // Note that 'oblique 0deg' represents 'normal', and will serialize as such.
248    #[value_info(starts_with_keyword)]
249    Oblique(Angle),
250    #[animation(error)]
251    Italic,
252}
253
254impl<Angle: Zero> FontStyle<Angle> {
255    /// Return the 'normal' value, which is represented as 'oblique 0deg'.
256    pub fn normal() -> Self {
257        Self::Oblique(Angle::zero())
258    }
259}
260
261/// A generic value for the `font-size-adjust` property.
262///
263/// https://drafts.csswg.org/css-fonts-5/#font-size-adjust-prop
264#[allow(missing_docs)]
265#[repr(u8)]
266#[derive(
267    Animate,
268    Clone,
269    ComputeSquaredDistance,
270    Copy,
271    Debug,
272    Deserialize,
273    Hash,
274    MallocSizeOf,
275    PartialEq,
276    Serialize,
277    SpecifiedValueInfo,
278    ToAnimatedValue,
279    ToAnimatedZero,
280    ToComputedValue,
281    ToResolvedValue,
282    ToShmem,
283)]
284pub enum GenericFontSizeAdjust<Factor> {
285    #[animation(error)]
286    None,
287    #[value_info(starts_with_keyword)]
288    ExHeight(Factor),
289    #[value_info(starts_with_keyword)]
290    CapHeight(Factor),
291    #[value_info(starts_with_keyword)]
292    ChWidth(Factor),
293    #[value_info(starts_with_keyword)]
294    IcWidth(Factor),
295    #[value_info(starts_with_keyword)]
296    IcHeight(Factor),
297}
298
299impl<Factor: ToCss> ToCss for GenericFontSizeAdjust<Factor> {
300    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
301    where
302        W: Write,
303    {
304        let (prefix, value) = match self {
305            Self::None => return dest.write_str("none"),
306            Self::ExHeight(v) => ("", v),
307            Self::CapHeight(v) => ("cap-height ", v),
308            Self::ChWidth(v) => ("ch-width ", v),
309            Self::IcWidth(v) => ("ic-width ", v),
310            Self::IcHeight(v) => ("ic-height ", v),
311        };
312
313        dest.write_str(prefix)?;
314        value.to_css(dest)
315    }
316}
317
318impl<Factor: ToTyped> ToTyped for GenericFontSizeAdjust<Factor> {
319    fn to_typed(&self, dest: &mut ThinVec<TypedValue>) -> Result<(), ()> {
320        match self {
321            Self::None => {
322                dest.push(TypedValue::Keyword(KeywordValue(CssString::from("none"))));
323                Ok(())
324            },
325            Self::ExHeight(v) => v.to_typed(dest),
326            _ => Err(()),
327        }
328    }
329}
330
331/// A generic value for the `line-height` property.
332#[derive(
333    Animate,
334    Clone,
335    ComputeSquaredDistance,
336    Copy,
337    Debug,
338    Deserialize,
339    MallocSizeOf,
340    PartialEq,
341    Serialize,
342    SpecifiedValueInfo,
343    ToAnimatedValue,
344    ToCss,
345    ToShmem,
346    ToTyped,
347)]
348#[repr(C, u8)]
349pub enum GenericLineHeight<N, L> {
350    /// `normal`
351    Normal,
352    /// `<number>`
353    Number(N),
354    /// `<length-percentage>`
355    Length(L),
356}
357
358pub use self::GenericLineHeight as LineHeight;
359
360impl<N, L> ToAnimatedZero for LineHeight<N, L> {
361    #[inline]
362    fn to_animated_zero(&self) -> Result<Self, ()> {
363        Err(())
364    }
365}
366
367impl<N, L> LineHeight<N, L> {
368    /// Returns `normal`.
369    #[inline]
370    pub fn normal() -> Self {
371        LineHeight::Normal
372    }
373
374    /// Returns whether the value is `normal`.
375    #[inline]
376    pub fn is_normal(&self) -> bool {
377        matches!(self, Self::Normal)
378    }
379}