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)]
42pub struct FeatureTagValue<Integer> {
43    /// A four-character tag, packed into a u32 (one byte per character).
44    pub tag: FontTag,
45    /// The actual value.
46    pub value: Integer,
47}
48
49impl<T> TaggedFontValue for FeatureTagValue<T> {
50    fn tag(&self) -> FontTag {
51        self.tag
52    }
53}
54
55impl<Integer> ToCss for FeatureTagValue<Integer>
56where
57    Integer: One + ToCss + PartialEq,
58{
59    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
60    where
61        W: Write,
62    {
63        self.tag.to_css(dest)?;
64        // Don't serialize the default value.
65        if !self.value.is_one() {
66            dest.write_char(' ')?;
67            self.value.to_css(dest)?;
68        }
69
70        Ok(())
71    }
72}
73
74/// Variation setting for a single feature, see:
75///
76/// https://drafts.csswg.org/css-fonts-4/#font-variation-settings-def
77#[derive(
78    Animate,
79    Clone,
80    ComputeSquaredDistance,
81    Debug,
82    Deserialize,
83    Eq,
84    MallocSizeOf,
85    PartialEq,
86    Serialize,
87    SpecifiedValueInfo,
88    ToAnimatedValue,
89    ToComputedValue,
90    ToCss,
91    ToResolvedValue,
92    ToShmem,
93)]
94pub struct VariationValue<Number> {
95    /// A four-character tag, packed into a u32 (one byte per character).
96    #[animation(constant)]
97    pub tag: FontTag,
98    /// The actual value.
99    pub value: Number,
100}
101
102impl<T> TaggedFontValue for VariationValue<T> {
103    fn tag(&self) -> FontTag {
104        self.tag
105    }
106}
107
108/// A value both for font-variation-settings and font-feature-settings.
109#[derive(
110    Clone,
111    Debug,
112    Deserialize,
113    Eq,
114    Hash,
115    MallocSizeOf,
116    PartialEq,
117    Serialize,
118    SpecifiedValueInfo,
119    ToAnimatedValue,
120    ToCss,
121    ToResolvedValue,
122    ToShmem,
123    ToTyped,
124)]
125#[css(comma)]
126#[typed(todo_derive_fields)]
127pub struct FontSettings<T>(#[css(if_empty = "normal", iterable)] pub Box<[T]>);
128
129impl<T> FontSettings<T> {
130    /// Default value of font settings as `normal`.
131    #[inline]
132    pub fn normal() -> Self {
133        FontSettings(vec![].into_boxed_slice())
134    }
135}
136
137impl<T: Parse> Parse for FontSettings<T> {
138    /// https://drafts.csswg.org/css-fonts-4/#descdef-font-face-font-feature-settings
139    /// https://drafts.csswg.org/css-fonts-4/#font-variation-settings-def
140    fn parse<'i, 't>(
141        context: &ParserContext,
142        input: &mut Parser<'i, 't>,
143    ) -> Result<Self, ParseError<'i>> {
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_boxed_slice(),
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///
165#[derive(
166    Clone,
167    Copy,
168    Deserialize,
169    Eq,
170    Hash,
171    MallocSizeOf,
172    PartialEq,
173    Serialize,
174    SpecifiedValueInfo,
175    ToAnimatedValue,
176    ToComputedValue,
177    ToResolvedValue,
178    ToShmem,
179)]
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<'i, 't>(
212        _context: &ParserContext,
213        input: &mut Parser<'i, 't>,
214    ) -> Result<Self, ParseError<'i>> {
215        let location = input.current_source_location();
216        let tag = input.expect_string()?;
217
218        // allowed strings of length 4 containing chars: <U+20, U+7E>
219        if tag.len() != 4 || tag.as_bytes().iter().any(|c| *c < b' ' || *c > b'~') {
220            return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
221        }
222
223        let mut raw = Cursor::new(tag.as_bytes());
224        Ok(FontTag(raw.read_u32::<BigEndian>().unwrap()))
225    }
226}
227
228/// A generic value for the `font-style` property.
229///
230/// https://drafts.csswg.org/css-fonts-4/#font-style-prop
231#[allow(missing_docs)]
232#[derive(
233    Animate,
234    Clone,
235    ComputeSquaredDistance,
236    Copy,
237    Debug,
238    Deserialize,
239    Hash,
240    MallocSizeOf,
241    PartialEq,
242    Serialize,
243    SpecifiedValueInfo,
244    ToAnimatedValue,
245    ToAnimatedZero,
246    ToResolvedValue,
247    ToShmem,
248)]
249#[value_info(other_values = "normal")]
250pub enum FontStyle<Angle> {
251    // Note that 'oblique 0deg' represents 'normal', and will serialize as such.
252    #[value_info(starts_with_keyword)]
253    Oblique(Angle),
254    #[animation(error)]
255    Italic,
256}
257
258impl<Angle: Zero> FontStyle<Angle> {
259    /// Return the 'normal' value, which is represented as 'oblique 0deg'.
260    pub fn normal() -> Self {
261        Self::Oblique(Angle::zero())
262    }
263}
264
265/// A generic value for the `font-size-adjust` property.
266///
267/// https://drafts.csswg.org/css-fonts-5/#font-size-adjust-prop
268#[allow(missing_docs)]
269#[repr(u8)]
270#[derive(
271    Animate,
272    Clone,
273    ComputeSquaredDistance,
274    Copy,
275    Debug,
276    Deserialize,
277    Hash,
278    MallocSizeOf,
279    PartialEq,
280    Serialize,
281    SpecifiedValueInfo,
282    ToAnimatedValue,
283    ToAnimatedZero,
284    ToComputedValue,
285    ToResolvedValue,
286    ToShmem,
287)]
288pub enum GenericFontSizeAdjust<Factor> {
289    #[animation(error)]
290    None,
291    #[value_info(starts_with_keyword)]
292    ExHeight(Factor),
293    #[value_info(starts_with_keyword)]
294    CapHeight(Factor),
295    #[value_info(starts_with_keyword)]
296    ChWidth(Factor),
297    #[value_info(starts_with_keyword)]
298    IcWidth(Factor),
299    #[value_info(starts_with_keyword)]
300    IcHeight(Factor),
301}
302
303impl<Factor: ToCss> ToCss for GenericFontSizeAdjust<Factor> {
304    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
305    where
306        W: Write,
307    {
308        let (prefix, value) = match self {
309            Self::None => return dest.write_str("none"),
310            Self::ExHeight(v) => ("", v),
311            Self::CapHeight(v) => ("cap-height ", v),
312            Self::ChWidth(v) => ("ch-width ", v),
313            Self::IcWidth(v) => ("ic-width ", v),
314            Self::IcHeight(v) => ("ic-height ", v),
315        };
316
317        dest.write_str(prefix)?;
318        value.to_css(dest)
319    }
320}
321
322impl<Factor: ToTyped> ToTyped for GenericFontSizeAdjust<Factor> {
323    fn to_typed(&self, dest: &mut ThinVec<TypedValue>) -> Result<(), ()> {
324        match self {
325            Self::None => {
326                dest.push(TypedValue::Keyword(KeywordValue(CssString::from("none"))));
327                Ok(())
328            },
329            Self::ExHeight(v) => v.to_typed(dest),
330            _ => Err(()),
331        }
332    }
333}
334
335/// A generic value for the `line-height` property.
336#[derive(
337    Animate,
338    Clone,
339    ComputeSquaredDistance,
340    Copy,
341    Debug,
342    Deserialize,
343    MallocSizeOf,
344    PartialEq,
345    Serialize,
346    SpecifiedValueInfo,
347    ToAnimatedValue,
348    ToCss,
349    ToShmem,
350    Parse,
351    ToTyped,
352)]
353#[repr(C, u8)]
354pub enum GenericLineHeight<N, L> {
355    /// `normal`
356    Normal,
357    /// `<number>`
358    Number(N),
359    /// `<length-percentage>`
360    Length(L),
361}
362
363pub use self::GenericLineHeight as LineHeight;
364
365impl<N, L> ToAnimatedZero for LineHeight<N, L> {
366    #[inline]
367    fn to_animated_zero(&self) -> Result<Self, ()> {
368        Err(())
369    }
370}
371
372impl<N, L> LineHeight<N, L> {
373    /// Returns `normal`.
374    #[inline]
375    pub fn normal() -> Self {
376        LineHeight::Normal
377    }
378
379    /// Returns whether the value is `normal`.
380    #[inline]
381    pub fn is_normal(&self) -> bool {
382        matches!(self, Self::Normal)
383    }
384}