Add font-variant-{alternates,east-asian,ligutures,numeric}.

This commit is contained in:
Hiroyuki Ikezoe 2017-04-13 16:50:12 +09:00
parent 26c45275ff
commit 1eb9a0865a
2 changed files with 582 additions and 1 deletions

View file

@ -293,6 +293,26 @@ def set_gecko_property(ffi_name, expr):
}
</%def>
<%def name="impl_bitflags_setter(ident, gecko_ffi_name, bit_map, gecko_bit_prefix, cast_type='u8')">
#[allow(non_snake_case)]
pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) {
% for gecko_bit in bit_map.values():
use gecko_bindings::structs::${gecko_bit_prefix}${gecko_bit};
% endfor
let mut bits: ${cast_type} = 0;
// FIXME: if we ensure that the Servo bitflags storage is the same
// as Gecko's one, we can just copy it.
% for servo_bit, gecko_bit in bit_map.iteritems():
if v.contains(longhands::${ident}::${servo_bit}) {
bits |= ${gecko_bit_prefix}${gecko_bit} as ${cast_type};
}
% endfor
self.gecko.${gecko_ffi_name} = bits as ${cast_type};
}
</%def>
/// Convert a Servo color into an nscolor; with currentColor as 0
///
@ -1262,7 +1282,9 @@ fn static_assert() {
<%
skip_font_longhands = """font-family font-size font-size-adjust font-weight
font-synthesis -x-lang font-language-override"""
font-synthesis -x-lang font-variant-alternates
font-variant-east-asian font-variant-ligatures
font-variant-numeric font-language-override"""
%>
<%self:impl_trait style_struct_name="Font"
skip_longhands="${skip_font_longhands}"
@ -1408,6 +1430,76 @@ fn static_assert() {
self.gecko.mFont.languageOverride = v.0;
}
${impl_simple_copy('font_language_override', 'mFont.languageOverride')}
<% font_variant_alternates_map = { "HISTORICAL_FORMS": "HISTORICAL",
"STYLISTIC": "STYLISTIC",
"STYLESET": "STYLESET",
"CHARACTER_VARIANT": "CHARACTER_VARIANT",
"SWASH": "SWASH",
"ORNAMENTS": "ORNAMENTS",
"ANNOTATION": "ANNOTATION" } %>
// FIXME: Set alternateValues as well.
// self.gecko.mFont.alternateValues = xxx;
${impl_bitflags_setter('font_variant_alternates',
'mFont.variantAlternates',
font_variant_alternates_map,
'NS_FONT_VARIANT_ALTERNATES_',
cast_type='u16')}
#[allow(non_snake_case)]
pub fn copy_font_variant_alternates_from(&mut self, other: &Self) {
self.gecko.mFont.variantAlternates = other.gecko.mFont.variantAlternates;
// FIXME: Copy alternateValues as well.
// self.gecko.mFont.alternateValues = other.gecko.mFont.alternateValues;
}
// servo_bit: gecko_bit
<% font_variant_ligatures_map = { "NONE": "NONE",
"COMMON_LIGATURES": "COMMON",
"NO_COMMON_LIGATURES": "NO_COMMON",
"DISCRETIONARY_LIGATURES": "DISCRETIONARY",
"NO_DISCRETIONARY_LIGATURES": "NO_DISCRETIONARY",
"HISTORICAL_LIGATURES": "HISTORICAL",
"NO_HISTORICAL_LIGATURES": "NO_HISTORICAL",
"CONTEXTUAL": "CONTEXTUAL",
"NO_CONTEXTUAL": "NO_CONTEXTUAL" } %>
${impl_bitflags_setter('font_variant_ligatures',
'mFont.variantLigatures',
font_variant_ligatures_map,
'NS_FONT_VARIANT_LIGATURES_',
cast_type='u16')}
${impl_simple_copy('font_variant_ligatures', 'mFont.variantLigatures')}
// servo_bit: gecko_bit
<% font_variant_east_asian_map = { "JIS78": "JIS78",
"JIS83": "JIS83",
"JIS90": "JIS90",
"JIS04": "JIS04",
"SIMPLIFIED": "SIMPLIFIED",
"TRADITIONAL": "TRADITIONAL",
"FULL_WIDTH": "FULL_WIDTH",
"PROPORTIONAL_WIDTH": "PROP_WIDTH",
"RUBY": "RUBY" } %>
${impl_bitflags_setter('font_variant_east_asian',
'mFont.variantEastAsian',
font_variant_east_asian_map,
'NS_FONT_VARIANT_EAST_ASIAN_',
cast_type='u16')}
${impl_simple_copy('font_variant_east_asian', 'mFont.variantEastAsian')}
// servo_bit: gecko_bit
<% font_variant_numeric_map = { "LINING_NUMS": "LINING",
"OLDSTYLE_NUMS": "OLDSTYLE",
"PROPORTIONAL_NUMS": "PROPORTIONAL",
"TABULAR_NUMS": "TABULAR",
"DIAGONAL_FRACTIONS": "DIAGONAL_FRACTIONS",
"STACKED_FRACTIONS": "STACKED_FRACTIONS",
"SLASHED_ZERO": "SLASHZERO",
"ORDINAL": "ORDINAL" } %>
${impl_bitflags_setter('font_variant_numeric',
'mFont.variantNumeric',
font_variant_numeric_map,
'NS_FONT_VARIANT_NUMERIC_')}
${impl_simple_copy('font_variant_numeric', 'mFont.variantNumeric')}
</%self:impl_trait>
<%def name="impl_copy_animation_or_transition_value(type, ident, gecko_ffi_name)">

View file

@ -893,6 +893,495 @@ ${helpers.single_keyword("font-kerning",
spec="https://drafts.csswg.org/css-fonts/#propdef-font-stretch",
animation_type="none")}
/// FIXME: Implement proper handling of each values.
/// https://github.com/servo/servo/issues/15957
<%helpers:longhand name="font-variant-alternates" products="gecko" animation_type="none"
spec="https://drafts.csswg.org/css-fonts/#propdef-font-variant-alternates">
use std::fmt;
use style_traits::ToCss;
use values::HasViewportPercentage;
use values::computed::ComputedValueAsSpecified;
impl ComputedValueAsSpecified for SpecifiedValue {}
no_viewport_percentage!(SpecifiedValue);
bitflags! {
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub flags SpecifiedValue: u8 {
const NORMAL = 0,
const HISTORICAL_FORMS = 0x01,
const STYLISTIC = 0x02,
const STYLESET = 0x04,
const CHARACTER_VARIANT = 0x08,
const SWASH = 0x10,
const ORNAMENTS = 0x20,
const ANNOTATION = 0x40,
}
}
impl ToCss for SpecifiedValue {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
if self.is_empty() {
return dest.write_str("normal")
}
let mut has_any = false;
macro_rules! write_value {
($ident:ident => $str:expr) => {
if self.intersects($ident) {
if has_any {
try!(dest.write_str(" "));
}
has_any = true;
try!(dest.write_str($str));
}
}
}
write_value!(HISTORICAL_FORMS => "historical-forms");
write_value!(STYLISTIC => "stylistic");
write_value!(STYLESET => "styleset");
write_value!(CHARACTER_VARIANT => "character-variant");
write_value!(SWASH => "swash");
write_value!(ORNAMENTS => "ornaments");
write_value!(ANNOTATION => "annotation");
debug_assert!(has_any);
Ok(())
}
}
pub mod computed_value {
pub type T = super::SpecifiedValue;
}
#[inline]
pub fn get_initial_value() -> computed_value::T {
computed_value::T::empty()
}
#[inline]
pub fn get_initial_specified_value() -> SpecifiedValue {
SpecifiedValue::empty()
}
/// normal |
/// [ stylistic(<feature-value-name>) ||
/// historical-forms ||
/// styleset(<feature-value-name> #) ||
/// character-variant(<feature-value-name> #) ||
/// swash(<feature-value-name>) ||
/// ornaments(<feature-value-name>) ||
/// annotation(<feature-value-name>) ]
pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
let mut result = SpecifiedValue::empty();
if input.try(|input| input.expect_ident_matching("normal")).is_ok() {
return Ok(result)
}
while let Ok(ident) = input.try(|input| input.expect_ident()) {
let flag = match_ignore_ascii_case! { &ident,
"stylistic" => STYLISTIC,
"historical-forms" => HISTORICAL_FORMS,
"styleset" => STYLESET,
"character-variant" => CHARACTER_VARIANT,
"swash" => SWASH,
"ornaments" => ORNAMENTS,
"annotation" => ANNOTATION,
_ => return Err(()),
};
if result.intersects(flag) {
return Err(())
}
result.insert(flag);
}
if !result.is_empty() {
Ok(result)
} else {
Err(())
}
}
</%helpers:longhand>
macro_rules! exclusive_value {
(($value:ident, $set:expr) => $ident:ident) => {
if $value.intersects($set) {
return Err(())
} else {
$ident
}
}
}
<%helpers:longhand name="font-variant-east-asian" products="gecko" animation_type="none"
spec="https://drafts.csswg.org/css-fonts/#propdef-font-variant-east-asian">
use std::fmt;
use style_traits::ToCss;
use values::HasViewportPercentage;
use values::computed::ComputedValueAsSpecified;
impl ComputedValueAsSpecified for SpecifiedValue {}
no_viewport_percentage!(SpecifiedValue);
bitflags! {
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub flags SpecifiedValue: u16 {
const NORMAL = 0,
const JIS78 = 0x01,
const JIS83 = 0x02,
const JIS90 = 0x04,
const JIS04 = 0x08,
const SIMPLIFIED = 0x10,
const TRADITIONAL = 0x20,
const FULL_WIDTH = 0x40,
const PROPORTIONAL_WIDTH = 0x80,
const RUBY = 0x100,
}
}
impl ToCss for SpecifiedValue {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
if self.is_empty() {
return dest.write_str("normal")
}
let mut has_any = false;
macro_rules! write_value {
($ident:ident => $str:expr) => {
if self.intersects($ident) {
if has_any {
try!(dest.write_str(" "));
}
has_any = true;
try!(dest.write_str($str));
}
}
}
write_value!(JIS78 => "jis78");
write_value!(JIS83 => "jis83");
write_value!(JIS90 => "jis90");
write_value!(JIS04 => "jis04");
write_value!(SIMPLIFIED => "simplified");
write_value!(TRADITIONAL => "traditional");
write_value!(FULL_WIDTH => "full-width");
write_value!(PROPORTIONAL_WIDTH => "proportional-width");
write_value!(RUBY => "ruby");
debug_assert!(has_any);
Ok(())
}
}
pub mod computed_value {
pub type T = super::SpecifiedValue;
}
#[inline]
pub fn get_initial_value() -> computed_value::T {
computed_value::T::empty()
}
#[inline]
pub fn get_initial_specified_value() -> SpecifiedValue {
SpecifiedValue::empty()
}
/// normal | [ <east-asian-variant-values> || <east-asian-width-values> || ruby ]
/// <east-asian-variant-values> = [ jis78 | jis83 | jis90 | jis04 | simplified | traditional ]
/// <east-asian-width-values> = [ full-width | proportional-width ]
<% east_asian_variant_values = "JIS78 | JIS83 | JIS90 | JIS04 | SIMPLIFIED | TRADITIONAL" %>
<% east_asian_width_values = "FULL_WIDTH | PROPORTIONAL_WIDTH" %>
pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
let mut result = SpecifiedValue::empty();
if input.try(|input| input.expect_ident_matching("normal")).is_ok() {
return Ok(result)
}
while let Ok(ident) = input.try(|input| input.expect_ident()) {
let flag = match_ignore_ascii_case! { &ident,
"jis78" =>
exclusive_value!((result, ${east_asian_variant_values}) => JIS78),
"jis83" =>
exclusive_value!((result, ${east_asian_variant_values}) => JIS83),
"jis90" =>
exclusive_value!((result, ${east_asian_variant_values}) => JIS90),
"jis04" =>
exclusive_value!((result, ${east_asian_variant_values}) => JIS04),
"simplified" =>
exclusive_value!((result, ${east_asian_variant_values}) => SIMPLIFIED),
"traditional" =>
exclusive_value!((result, ${east_asian_variant_values}) => TRADITIONAL),
"full-width" =>
exclusive_value!((result, ${east_asian_width_values}) => FULL_WIDTH),
"proportional-width" =>
exclusive_value!((result, ${east_asian_width_values}) => PROPORTIONAL_WIDTH),
"ruby" =>
exclusive_value!((result, RUBY) => RUBY),
_ => return Err(()),
};
result.insert(flag);
}
if !result.is_empty() {
Ok(result)
} else {
Err(())
}
}
</%helpers:longhand>
<%helpers:longhand name="font-variant-ligatures" products="gecko" animation_type="none"
spec="https://drafts.csswg.org/css-fonts/#propdef-font-variant-ligatures">
use std::fmt;
use style_traits::ToCss;
use values::HasViewportPercentage;
use values::computed::ComputedValueAsSpecified;
impl ComputedValueAsSpecified for SpecifiedValue {}
no_viewport_percentage!(SpecifiedValue);
bitflags! {
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub flags SpecifiedValue: u16 {
const NORMAL = 0,
const NONE = 0x01,
const COMMON_LIGATURES = 0x02,
const NO_COMMON_LIGATURES = 0x04,
const DISCRETIONARY_LIGATURES = 0x08,
const NO_DISCRETIONARY_LIGATURES = 0x10,
const HISTORICAL_LIGATURES = 0x20,
const NO_HISTORICAL_LIGATURES = 0x40,
const CONTEXTUAL = 0x80,
const NO_CONTEXTUAL = 0x100,
}
}
impl ToCss for SpecifiedValue {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
if self.is_empty() {
return dest.write_str("normal")
}
if self.contains(NONE) {
return dest.write_str("none")
}
let mut has_any = false;
macro_rules! write_value {
($ident:ident => $str:expr) => {
if self.intersects($ident) {
if has_any {
try!(dest.write_str(" "));
}
has_any = true;
try!(dest.write_str($str));
}
}
}
write_value!(COMMON_LIGATURES => "common-ligatures");
write_value!(NO_COMMON_LIGATURES => "no-common-ligatures");
write_value!(DISCRETIONARY_LIGATURES => "discretionary-ligatures");
write_value!(NO_DISCRETIONARY_LIGATURES => "no-discretionary-ligatures");
write_value!(HISTORICAL_LIGATURES => "historical-ligatures");
write_value!(NO_HISTORICAL_LIGATURES => "no-historical-ligatures");
write_value!(CONTEXTUAL => "contextual");
write_value!(NO_CONTEXTUAL => "no-contextual");
debug_assert!(has_any);
Ok(())
}
}
pub mod computed_value {
pub type T = super::SpecifiedValue;
}
#[inline]
pub fn get_initial_value() -> computed_value::T {
computed_value::T::empty()
}
#[inline]
pub fn get_initial_specified_value() -> SpecifiedValue {
SpecifiedValue::empty()
}
/// normal | none |
/// [ <common-lig-values> ||
/// <discretionary-lig-values> ||
/// <historical-lig-values> ||
/// <contextual-alt-values> ]
/// <common-lig-values> = [ common-ligatures | no-common-ligatures ]
/// <discretionary-lig-values> = [ discretionary-ligatures | no-discretionary-ligatures ]
/// <historical-lig-values> = [ historical-ligatures | no-historical-ligatures ]
/// <contextual-alt-values> = [ contextual | no-contextual ]
<% common_lig_values = "COMMON_LIGATURES | NO_COMMON_LIGATURES" %>
<% discretionary_lig_values = "DISCRETIONARY_LIGATURES | NO_DISCRETIONARY_LIGATURES" %>
<% historical_lig_values = "HISTORICAL_LIGATURES | NO_HISTORICAL_LIGATURES" %>
<% contextual_alt_values = "CONTEXTUAL | NO_CONTEXTUAL" %>
pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
let mut result = SpecifiedValue::empty();
if input.try(|input| input.expect_ident_matching("normal")).is_ok() {
return Ok(result)
}
if input.try(|input| input.expect_ident_matching("none")).is_ok() {
return Ok(NONE)
}
while let Ok(ident) = input.try(|input| input.expect_ident()) {
let flag = match_ignore_ascii_case! { &ident,
"common-ligatures" =>
exclusive_value!((result, ${common_lig_values}) => COMMON_LIGATURES),
"no-common-ligatures" =>
exclusive_value!((result, ${common_lig_values}) => NO_COMMON_LIGATURES),
"discretionary-ligatures" =>
exclusive_value!((result, ${discretionary_lig_values}) => DISCRETIONARY_LIGATURES),
"no-discretionary-ligatures" =>
exclusive_value!((result, ${discretionary_lig_values}) => NO_DISCRETIONARY_LIGATURES),
"historical-ligatures" =>
exclusive_value!((result, ${historical_lig_values}) => HISTORICAL_LIGATURES),
"no-historical-ligatures" =>
exclusive_value!((result, ${historical_lig_values}) => NO_HISTORICAL_LIGATURES),
"contextual" =>
exclusive_value!((result, ${contextual_alt_values}) => CONTEXTUAL),
"no-contextual" =>
exclusive_value!((result, ${contextual_alt_values}) => NO_CONTEXTUAL),
_ => return Err(()),
};
result.insert(flag);
}
if !result.is_empty() {
Ok(result)
} else {
Err(())
}
}
</%helpers:longhand>
<%helpers:longhand name="font-variant-numeric" products="gecko" animation_type="none"
spec="https://drafts.csswg.org/css-fonts/#propdef-font-variant-numeric">
use std::fmt;
use style_traits::ToCss;
use values::HasViewportPercentage;
use values::computed::ComputedValueAsSpecified;
impl ComputedValueAsSpecified for SpecifiedValue {}
no_viewport_percentage!(SpecifiedValue);
bitflags! {
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub flags SpecifiedValue: u8 {
const NORMAL = 0,
const LINING_NUMS = 0x01,
const OLDSTYLE_NUMS = 0x02,
const PROPORTIONAL_NUMS = 0x04,
const TABULAR_NUMS = 0x08,
const DIAGONAL_FRACTIONS = 0x10,
const STACKED_FRACTIONS = 0x20,
const SLASHED_ZERO = 0x40,
const ORDINAL = 0x80,
}
}
impl ToCss for SpecifiedValue {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
if self.is_empty() {
return dest.write_str("normal")
}
let mut has_any = false;
macro_rules! write_value {
($ident:ident => $str:expr) => {
if self.intersects($ident) {
if has_any {
try!(dest.write_str(" "));
}
has_any = true;
try!(dest.write_str($str));
}
}
}
write_value!(LINING_NUMS => "lining-nums");
write_value!(OLDSTYLE_NUMS => "oldstyle-nums");
write_value!(PROPORTIONAL_NUMS => "proportional-nums");
write_value!(TABULAR_NUMS => "tabular-nums");
write_value!(DIAGONAL_FRACTIONS => "diagonal-fractions");
write_value!(STACKED_FRACTIONS => "stacked-fractions");
write_value!(SLASHED_ZERO => "slashed-zero");
write_value!(ORDINAL => "ordinal");
debug_assert!(has_any);
Ok(())
}
}
pub mod computed_value {
pub type T = super::SpecifiedValue;
}
#[inline]
pub fn get_initial_value() -> computed_value::T {
computed_value::T::empty()
}
#[inline]
pub fn get_initial_specified_value() -> SpecifiedValue {
SpecifiedValue::empty()
}
/// normal |
/// [ <numeric-figure-values> ||
/// <numeric-spacing-values> ||
/// <numeric-fraction-values> ||
/// ordinal ||
/// slashed-zero ]
/// <numeric-figure-values> = [ lining-nums | oldstyle-nums ]
/// <numeric-spacing-values> = [ proportional-nums | tabular-nums ]
/// <numeric-fraction-values> = [ diagonal-fractions | stacked-fractions ]
<% numeric_figure_values = "LINING_NUMS | OLDSTYLE_NUMS" %>
<% numeric_spacing_values = "PROPORTIONAL_NUMS | TABULAR_NUMS" %>
<% numeric_fraction_values = "DIAGONAL_FRACTIONS | STACKED_FRACTIONS" %>
pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
let mut result = SpecifiedValue::empty();
if input.try(|input| input.expect_ident_matching("normal")).is_ok() {
return Ok(result)
}
while let Ok(ident) = input.try(|input| input.expect_ident()) {
let flag = match_ignore_ascii_case! { &ident,
"ordinal" =>
exclusive_value!((result, ORDINAL) => ORDINAL),
"slashed-zero" =>
exclusive_value!((result, SLASHED_ZERO) => SLASHED_ZERO),
"lining-nums" =>
exclusive_value!((result, ${numeric_figure_values}) => LINING_NUMS ),
"oldstyle-nums" =>
exclusive_value!((result, ${numeric_figure_values}) => OLDSTYLE_NUMS ),
"proportional-nums" =>
exclusive_value!((result, ${numeric_spacing_values}) => PROPORTIONAL_NUMS ),
"tabular-nums" =>
exclusive_value!((result, ${numeric_spacing_values}) => TABULAR_NUMS ),
"diagonal-fractions" =>
exclusive_value!((result, ${numeric_fraction_values}) => DIAGONAL_FRACTIONS ),
"stacked-fractions" =>
exclusive_value!((result, ${numeric_fraction_values}) => STACKED_FRACTIONS ),
_ => return Err(()),
};
result.insert(flag);
}
if !result.is_empty() {
Ok(result)
} else {
Err(())
}
}
</%helpers:longhand>
${helpers.single_keyword("font-variant-position",
"normal sub super",
products="gecko",