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 
7 use crate::parser::{Parse, ParserContext};
8 use crate::One;
9 use byteorder::{BigEndian, ReadBytesExt};
10 use cssparser::Parser;
11 use std::fmt::{self, Write};
12 use std::io::Cursor;
13 use style_traits::{CssWriter, ParseError};
14 use style_traits::{StyleParseErrorKind, ToCss};
15 
16 /// https://drafts.csswg.org/css-fonts-4/#feature-tag-value
17 #[derive(
18     Clone,
19     Debug,
20     Eq,
21     MallocSizeOf,
22     PartialEq,
23     SpecifiedValueInfo,
24     ToComputedValue,
25     ToResolvedValue,
26     ToShmem,
27 )]
28 pub struct FeatureTagValue<Integer> {
29     /// A four-character tag, packed into a u32 (one byte per character).
30     pub tag: FontTag,
31     /// The actual value.
32     pub value: Integer,
33 }
34 
35 impl<Integer> ToCss for FeatureTagValue<Integer>
36 where
37     Integer: One + ToCss + PartialEq,
38 {
to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: Write,39     fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
40     where
41         W: Write,
42     {
43         self.tag.to_css(dest)?;
44         // Don't serialize the default value.
45         if !self.value.is_one() {
46             dest.write_char(' ')?;
47             self.value.to_css(dest)?;
48         }
49 
50         Ok(())
51     }
52 }
53 
54 /// Variation setting for a single feature, see:
55 ///
56 /// https://drafts.csswg.org/css-fonts-4/#font-variation-settings-def
57 #[derive(
58     Animate,
59     Clone,
60     ComputeSquaredDistance,
61     Debug,
62     Eq,
63     MallocSizeOf,
64     PartialEq,
65     SpecifiedValueInfo,
66     ToComputedValue,
67     ToCss,
68     ToResolvedValue,
69     ToShmem,
70 )]
71 pub struct VariationValue<Number> {
72     /// A four-character tag, packed into a u32 (one byte per character).
73     #[animation(constant)]
74     pub tag: FontTag,
75     /// The actual value.
76     pub value: Number,
77 }
78 
79 /// A value both for font-variation-settings and font-feature-settings.
80 #[css(comma)]
81 #[derive(
82     Clone,
83     Debug,
84     Eq,
85     MallocSizeOf,
86     PartialEq,
87     SpecifiedValueInfo,
88     ToComputedValue,
89     ToCss,
90     ToResolvedValue,
91     ToShmem,
92 )]
93 pub struct FontSettings<T>(#[css(if_empty = "normal", iterable)] pub Box<[T]>);
94 
95 impl<T> FontSettings<T> {
96     /// Default value of font settings as `normal`.
97     #[inline]
normal() -> Self98     pub fn normal() -> Self {
99         FontSettings(vec![].into_boxed_slice())
100     }
101 }
102 
103 impl<T: Parse> Parse for FontSettings<T> {
104     /// https://drafts.csswg.org/css-fonts-4/#descdef-font-face-font-feature-settings
105     /// https://drafts.csswg.org/css-fonts-4/#font-variation-settings-def
parse<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<Self, ParseError<'i>>106     fn parse<'i, 't>(
107         context: &ParserContext,
108         input: &mut Parser<'i, 't>,
109     ) -> Result<Self, ParseError<'i>> {
110         if input.try(|i| i.expect_ident_matching("normal")).is_ok() {
111             return Ok(Self::normal());
112         }
113 
114         Ok(FontSettings(
115             input
116                 .parse_comma_separated(|i| T::parse(context, i))?
117                 .into_boxed_slice(),
118         ))
119     }
120 }
121 
122 /// A font four-character tag, represented as a u32 for convenience.
123 ///
124 /// See:
125 ///   https://drafts.csswg.org/css-fonts-4/#font-variation-settings-def
126 ///   https://drafts.csswg.org/css-fonts-4/#descdef-font-face-font-feature-settings
127 ///
128 #[derive(
129     Clone,
130     Copy,
131     Debug,
132     Eq,
133     MallocSizeOf,
134     PartialEq,
135     SpecifiedValueInfo,
136     ToComputedValue,
137     ToResolvedValue,
138     ToShmem,
139 )]
140 pub struct FontTag(pub u32);
141 
142 impl ToCss for FontTag {
to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: Write,143     fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
144     where
145         W: Write,
146     {
147         use byteorder::ByteOrder;
148         use std::str;
149 
150         let mut raw = [0u8; 4];
151         BigEndian::write_u32(&mut raw, self.0);
152         str::from_utf8(&raw).unwrap_or_default().to_css(dest)
153     }
154 }
155 
156 impl Parse for FontTag {
parse<'i, 't>( _context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<Self, ParseError<'i>>157     fn parse<'i, 't>(
158         _context: &ParserContext,
159         input: &mut Parser<'i, 't>,
160     ) -> Result<Self, ParseError<'i>> {
161         let location = input.current_source_location();
162         let tag = input.expect_string()?;
163 
164         // allowed strings of length 4 containing chars: <U+20, U+7E>
165         if tag.len() != 4 || tag.as_bytes().iter().any(|c| *c < b' ' || *c > b'~') {
166             return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
167         }
168 
169         let mut raw = Cursor::new(tag.as_bytes());
170         Ok(FontTag(raw.read_u32::<BigEndian>().unwrap()))
171     }
172 }
173 
174 /// A generic value for the `font-style` property.
175 ///
176 /// https://drafts.csswg.org/css-fonts-4/#font-style-prop
177 #[allow(missing_docs)]
178 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
179 #[derive(
180     Animate,
181     Clone,
182     ComputeSquaredDistance,
183     Copy,
184     Debug,
185     Hash,
186     MallocSizeOf,
187     PartialEq,
188     SpecifiedValueInfo,
189     ToAnimatedValue,
190     ToAnimatedZero,
191     ToResolvedValue,
192     ToShmem,
193 )]
194 pub enum FontStyle<Angle> {
195     #[animation(error)]
196     Normal,
197     #[animation(error)]
198     Italic,
199     #[value_info(starts_with_keyword)]
200     Oblique(Angle),
201 }
202