use cssparser::{ParseError, Parser, Token};
use crate::model::FontFace;
use crate::style::properties::{FontStyle, FontWeight, Length};
use super::keywords::parse_font_style;
pub(crate) fn parse_font_size(input: &mut Parser<'_, '_>) -> Option<Length> {
match input.next().ok()? {
Token::Dimension { value, unit, .. } => {
let length = match unit.as_ref() {
"px" => Length::Px(*value),
"em" => Length::Em(*value),
"rem" => Length::Rem(*value),
"%" => Length::Percent(*value),
"pt" => Length::Px(*value * 96.0 / 72.0), _ => return None,
};
Some(length)
}
Token::Percentage { unit_value, .. } => Some(Length::Percent(*unit_value * 100.0)),
Token::Number { value, .. } if *value == 0.0 => Some(Length::Px(0.0)),
Token::Ident(ident) => match ident.as_ref() {
"xx-small" => Some(Length::Rem(0.5625)), "x-small" => Some(Length::Rem(0.625)), "small" => Some(Length::Rem(0.8125)), "medium" => Some(Length::Rem(1.0)), "large" => Some(Length::Rem(1.125)), "x-large" => Some(Length::Rem(1.5)), "xx-large" => Some(Length::Rem(2.0)), "xxx-large" => Some(Length::Rem(3.0)), "smaller" => Some(Length::Em(0.833)), "larger" => Some(Length::Em(1.2)),
_ => None,
},
_ => None,
}
}
pub(crate) fn parse_line_height(input: &mut Parser<'_, '_>) -> Option<Length> {
match input.next().ok()? {
Token::Dimension { value, unit, .. } => {
let length = match unit.as_ref() {
"px" => Length::Px(*value),
"em" => Length::Em(*value),
"rem" => Length::Rem(*value),
"%" => Length::Percent(*value),
_ => return None,
};
Some(length)
}
Token::Percentage { unit_value, .. } => Some(Length::Percent(*unit_value * 100.0)),
Token::Number { value, .. } => Some(Length::Em(*value)),
Token::Ident(ident) => match ident.as_ref() {
"normal" => Some(Length::Auto),
_ => None,
},
_ => None,
}
}
pub(crate) fn parse_font_weight(input: &mut Parser<'_, '_>) -> Option<FontWeight> {
if let Ok(token) = input.try_parse(|i| i.expect_ident_cloned()) {
let weight = match token.as_ref() {
"normal" => FontWeight::NORMAL,
"bold" => FontWeight::BOLD,
"lighter" => FontWeight(300),
"bolder" => FontWeight(700),
_ => return None,
};
return Some(weight);
}
if let Ok(Token::Number {
int_value: Some(v), ..
}) = input.next()
{
let v = *v;
if (100..=900).contains(&v) && v % 100 == 0 {
return Some(FontWeight(v as u16));
}
}
None
}
pub(crate) fn parse_font_family(input: &mut Parser<'_, '_>) -> Option<String> {
let mut families = Vec::new();
loop {
if let Ok(token) = input.try_parse(|i| i.expect_string_cloned()) {
families.push(token.to_string());
} else if let Ok(token) = input.try_parse(|i| i.expect_ident_cloned()) {
families.push(token.to_string());
} else {
break;
}
if input.try_parse(|i| i.expect_comma()).is_err() {
break;
}
}
if families.is_empty() {
None
} else {
Some(families.join(", "))
}
}
pub(crate) fn parse_font_face_block(input: &mut Parser<'_, '_>) -> Option<FontFace> {
let mut font_family: Option<String> = None;
let mut font_weight = FontWeight::NORMAL;
let mut font_style = FontStyle::Normal;
let mut src: Option<String> = None;
while let Ok(name) = input.expect_ident_cloned() {
let name_str = name.as_ref();
if input.expect_colon().is_ok() {
match name_str {
"font-family" => {
font_family = parse_font_face_family(input);
}
"font-weight" => {
if let Some(w) = parse_font_weight(input) {
font_weight = w;
}
}
"font-style" => {
if let Some(s) = parse_font_style(input) {
font_style = s;
}
}
"src" => {
src = parse_font_face_src(input);
}
_ => {
while input.next().is_ok() {
if matches!(input.current_source_location().line, _) {
break;
}
}
}
}
let _ = input.try_parse(|i| i.expect_semicolon());
}
}
match (font_family, src) {
(Some(family), Some(source)) => {
Some(FontFace::new(family, font_weight, font_style, source))
}
_ => None,
}
}
fn parse_font_face_family(input: &mut Parser<'_, '_>) -> Option<String> {
if let Ok(s) = input.try_parse(|i| i.expect_string_cloned()) {
return Some(s.to_string());
}
if let Ok(s) = input.try_parse(|i| i.expect_ident_cloned()) {
return Some(s.to_string());
}
None
}
fn parse_font_face_src(input: &mut Parser<'_, '_>) -> Option<String> {
if let Ok(url) = input.try_parse(|i| i.expect_url_or_string()) {
return Some(url.as_ref().to_string());
}
if let Ok(url) = input.try_parse(|i| -> Result<String, ParseError<'_, ()>> {
i.expect_function_matching("url")?;
let url_str = i.parse_nested_block(|nested| {
nested
.expect_string_cloned()
.map(|s| s.to_string())
.map_err(|e| e.into())
})?;
Ok(url_str)
}) {
return Some(url);
}
None
}