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