1use 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
19pub trait TaggedFontValue {
22 fn tag(&self) -> FontTag;
24}
25
26#[derive(
28 Clone,
29 Debug,
30 Eq,
31 MallocSizeOf,
32 PartialEq,
33 SpecifiedValueInfo,
34 ToAnimatedValue,
35 ToComputedValue,
36 ToResolvedValue,
37 ToShmem,
38)]
39#[cfg_attr(feature = "servo", derive(Deserialize, Hash, Serialize))]
40pub struct FeatureTagValue<Integer> {
41 pub tag: FontTag,
43 pub value: Integer,
45}
46
47impl<T> TaggedFontValue for FeatureTagValue<T> {
48 fn tag(&self) -> FontTag {
49 self.tag
50 }
51}
52
53impl<Integer> ToCss for FeatureTagValue<Integer>
54where
55 Integer: One + ToCss + PartialEq,
56{
57 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
58 where
59 W: Write,
60 {
61 self.tag.to_css(dest)?;
62 if !self.value.is_one() {
64 dest.write_char(' ')?;
65 self.value.to_css(dest)?;
66 }
67
68 Ok(())
69 }
70}
71
72#[derive(
76 Animate,
77 Clone,
78 ComputeSquaredDistance,
79 Debug,
80 Eq,
81 MallocSizeOf,
82 PartialEq,
83 SpecifiedValueInfo,
84 ToAnimatedValue,
85 ToComputedValue,
86 ToCss,
87 ToResolvedValue,
88 ToShmem,
89)]
90#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
91pub struct VariationValue<Number> {
92 #[animation(constant)]
94 pub tag: FontTag,
95 pub value: Number,
97}
98
99impl<T> TaggedFontValue for VariationValue<T> {
100 fn tag(&self) -> FontTag {
101 self.tag
102 }
103}
104
105#[derive(
107 Clone,
108 Debug,
109 Eq,
110 MallocSizeOf,
111 PartialEq,
112 SpecifiedValueInfo,
113 ToAnimatedValue,
114 ToCss,
115 ToResolvedValue,
116 ToShmem,
117 ToTyped,
118)]
119#[cfg_attr(feature = "servo", derive(Deserialize, Hash, Serialize))]
120#[css(comma)]
121#[typed(todo_derive_fields)]
122pub struct FontSettings<T>(#[css(if_empty = "normal", iterable)] pub Box<[T]>);
123
124impl<T> FontSettings<T> {
125 #[inline]
127 pub fn normal() -> Self {
128 FontSettings(vec![].into_boxed_slice())
129 }
130}
131
132impl<T: Parse> Parse for FontSettings<T> {
133 fn parse<'i, 't>(
136 context: &ParserContext,
137 input: &mut Parser<'i, 't>,
138 ) -> Result<Self, ParseError<'i>> {
139 if input
140 .try_parse(|i| i.expect_ident_matching("normal"))
141 .is_ok()
142 {
143 return Ok(Self::normal());
144 }
145
146 Ok(FontSettings(
147 input
148 .parse_comma_separated(|i| T::parse(context, i))?
149 .into_boxed_slice(),
150 ))
151 }
152}
153
154#[derive(
161 Clone,
162 Copy,
163 Eq,
164 MallocSizeOf,
165 PartialEq,
166 SpecifiedValueInfo,
167 ToAnimatedValue,
168 ToComputedValue,
169 ToResolvedValue,
170 ToShmem,
171)]
172#[cfg_attr(feature = "servo", derive(Deserialize, Hash, Serialize))]
173pub struct FontTag(pub u32);
174
175impl fmt::Debug for FontTag {
176 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
177 let tag_bytes = self.0.to_be_bytes();
178
179 let mut tuple = f.debug_tuple("FontTag");
180 if let Ok(utf8_tag) = str::from_utf8(&tag_bytes) {
181 tuple.field(&utf8_tag);
182 } else {
183 tuple.field(&tag_bytes);
184 };
185 tuple.finish()
186 }
187}
188
189impl ToCss for FontTag {
190 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
191 where
192 W: Write,
193 {
194 use byteorder::ByteOrder;
195 use std::str;
196
197 let mut raw = [0u8; 4];
198 BigEndian::write_u32(&mut raw, self.0);
199 str::from_utf8(&raw).unwrap_or_default().to_css(dest)
200 }
201}
202
203impl Parse for FontTag {
204 fn parse<'i, 't>(
205 _context: &ParserContext,
206 input: &mut Parser<'i, 't>,
207 ) -> Result<Self, ParseError<'i>> {
208 let location = input.current_source_location();
209 let tag = input.expect_string()?;
210
211 if tag.len() != 4 || tag.as_bytes().iter().any(|c| *c < b' ' || *c > b'~') {
213 return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
214 }
215
216 let mut raw = Cursor::new(tag.as_bytes());
217 Ok(FontTag(raw.read_u32::<BigEndian>().unwrap()))
218 }
219}
220
221#[allow(missing_docs)]
225#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
226#[derive(
227 Animate,
228 Clone,
229 ComputeSquaredDistance,
230 Copy,
231 Debug,
232 Hash,
233 MallocSizeOf,
234 PartialEq,
235 SpecifiedValueInfo,
236 ToAnimatedValue,
237 ToAnimatedZero,
238 ToResolvedValue,
239 ToShmem,
240)]
241#[value_info(other_values = "normal")]
242pub enum FontStyle<Angle> {
243 #[value_info(starts_with_keyword)]
245 Oblique(Angle),
246 #[animation(error)]
247 Italic,
248}
249
250impl<Angle: Zero> FontStyle<Angle> {
251 pub fn normal() -> Self {
253 Self::Oblique(Angle::zero())
254 }
255}
256
257#[allow(missing_docs)]
261#[repr(u8)]
262#[derive(
263 Animate,
264 Clone,
265 ComputeSquaredDistance,
266 Copy,
267 Debug,
268 Hash,
269 MallocSizeOf,
270 PartialEq,
271 SpecifiedValueInfo,
272 ToAnimatedValue,
273 ToAnimatedZero,
274 ToComputedValue,
275 ToResolvedValue,
276 ToShmem,
277)]
278pub enum GenericFontSizeAdjust<Factor> {
279 #[animation(error)]
280 None,
281 #[value_info(starts_with_keyword)]
282 ExHeight(Factor),
283 #[value_info(starts_with_keyword)]
284 CapHeight(Factor),
285 #[value_info(starts_with_keyword)]
286 ChWidth(Factor),
287 #[value_info(starts_with_keyword)]
288 IcWidth(Factor),
289 #[value_info(starts_with_keyword)]
290 IcHeight(Factor),
291}
292
293impl<Factor: ToCss> ToCss for GenericFontSizeAdjust<Factor> {
294 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
295 where
296 W: Write,
297 {
298 let (prefix, value) = match self {
299 Self::None => return dest.write_str("none"),
300 Self::ExHeight(v) => ("", v),
301 Self::CapHeight(v) => ("cap-height ", v),
302 Self::ChWidth(v) => ("ch-width ", v),
303 Self::IcWidth(v) => ("ic-width ", v),
304 Self::IcHeight(v) => ("ic-height ", v),
305 };
306
307 dest.write_str(prefix)?;
308 value.to_css(dest)
309 }
310}
311
312impl<Factor: ToTyped> ToTyped for GenericFontSizeAdjust<Factor> {
313 fn to_typed(&self, dest: &mut ThinVec<TypedValue>) -> Result<(), ()> {
314 match self {
315 Self::None => {
316 dest.push(TypedValue::Keyword(KeywordValue(CssString::from("none"))));
317 Ok(())
318 },
319 Self::ExHeight(v) => v.to_typed(dest),
320 _ => Err(()),
321 }
322 }
323}
324
325#[derive(
327 Animate,
328 Clone,
329 ComputeSquaredDistance,
330 Copy,
331 Debug,
332 MallocSizeOf,
333 PartialEq,
334 SpecifiedValueInfo,
335 ToAnimatedValue,
336 ToCss,
337 ToShmem,
338 Parse,
339 ToTyped,
340)]
341#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
342#[repr(C, u8)]
343pub enum GenericLineHeight<N, L> {
344 Normal,
346 #[cfg(feature = "gecko")]
348 #[parse(condition = "ParserContext::in_ua_sheet")]
349 MozBlockHeight,
350 Number(N),
352 Length(L),
354}
355
356pub use self::GenericLineHeight as LineHeight;
357
358impl<N, L> ToAnimatedZero for LineHeight<N, L> {
359 #[inline]
360 fn to_animated_zero(&self) -> Result<Self, ()> {
361 Err(())
362 }
363}
364
365impl<N, L> LineHeight<N, L> {
366 #[inline]
368 pub fn normal() -> Self {
369 LineHeight::Normal
370 }
371}