diff --git a/gtk/gtkcssfontfeaturesvalue.c b/gtk/gtkcssfontfeaturesvalue.c new file mode 100644 index 0000000000..29a389ae70 --- /dev/null +++ b/gtk/gtkcssfontfeaturesvalue.c @@ -0,0 +1,296 @@ +/* GTK - The GIMP Toolkit + * Copyright (C) 2017 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Author: Matthias Clasen + */ + +#include "config.h" + +#include "gtkcsstypesprivate.h" +#include "gtkcssparserprivate.h" +#include "gtkcssnumbervalueprivate.h" +#include "gtkcssfontfeaturesvalueprivate.h" + +struct _GtkCssValue { + GTK_CSS_VALUE_BASE + GHashTable *features; +}; + +static GtkCssValue *default_font_features; + +static GtkCssValue *gtk_css_font_features_value_new_empty (void); + +static void +gtk_css_font_features_value_add_feature (GtkCssValue *value, + const char *name, + GtkCssValue *val) +{ + g_hash_table_insert (value->features, g_strdup (name), val); +} + + +static void +gtk_css_value_font_features_free (GtkCssValue *value) +{ + g_hash_table_unref (value->features); + + g_slice_free (GtkCssValue, value); +} + +static GtkCssValue * +gtk_css_value_font_features_compute (GtkCssValue *specified, + guint property_id, + GtkStyleProvider *provider, + GtkCssStyle *style, + GtkCssStyle *parent_style) +{ + GHashTableIter iter; + gpointer name, val; + GtkCssValue *computed_val; + GtkCssValue *result; + gboolean changes = FALSE; + + result = gtk_css_font_features_value_new_empty (); + + g_hash_table_iter_init (&iter, specified->features); + while (g_hash_table_iter_next (&iter, &name, &val)) + { + computed_val = _gtk_css_value_compute (val, property_id, provider, style, parent_style); + changes |= computed_val != val; + gtk_css_font_features_value_add_feature (result, name, computed_val); + } + + if (!changes) + { + _gtk_css_value_unref (result); + result = _gtk_css_value_ref (specified); + } + + return result; +} + +static gboolean +gtk_css_value_font_features_equal (const GtkCssValue *value1, + const GtkCssValue *value2) +{ + gpointer name, val1, val2; + GHashTableIter iter; + + if (g_hash_table_size (value1->features) != g_hash_table_size (value2->features)) + return FALSE; + + g_hash_table_iter_init (&iter, value1->features); + while (g_hash_table_iter_next (&iter, &name, &val1)) + { + val2 = g_hash_table_lookup (value2->features, name); + if (val2 == NULL) + return FALSE; + + if (!_gtk_css_value_equal (val1, val2)) + return FALSE; + } + + return TRUE; +} + +static GtkCssValue * +gtk_css_value_font_features_transition (GtkCssValue *start, + GtkCssValue *end, + guint property_id, + double progress) +{ + const char *name; + GtkCssValue *start_val, *end_val; + GHashTableIter iter; + GtkCssValue *result, *transition; + + /* XXX: For value that are only in start or end but not both, + * we don't transition but just keep the value. + * That causes an abrupt transition to the new value at the end. + */ + + result = gtk_css_font_features_value_new_empty (); + + g_hash_table_iter_init (&iter, start->features); + while (g_hash_table_iter_next (&iter, (gpointer *)&name, (gpointer *)&start_val)) + { + end_val = g_hash_table_lookup (end->features, name); + if (end_val == NULL) + transition = _gtk_css_value_ref (start_val); + else + transition = _gtk_css_value_transition (start_val, end_val, property_id, progress); + + gtk_css_font_features_value_add_feature (result, name, transition); + } + + g_hash_table_iter_init (&iter, end->features); + while (g_hash_table_iter_next (&iter, (gpointer *)&name, (gpointer *)&end_val)) + { + start_val = g_hash_table_lookup (start->features, name); + if (start_val != NULL) + continue; + + gtk_css_font_features_value_add_feature (result, name, _gtk_css_value_ref (end_val)); + } + + return result; +} + +static void +gtk_css_value_font_features_print (const GtkCssValue *value, + GString *string) +{ + GHashTableIter iter; + const char *name; + GtkCssValue *val; + gboolean first = TRUE; + + if (value == default_font_features) + { + g_string_append (string, "normal"); + return; + } + + g_hash_table_iter_init (&iter, value->features); + while (g_hash_table_iter_next (&iter, (gpointer *)&name, (gpointer *)&val)) + { + if (first) + first = FALSE; + else + g_string_append (string, ", "); + g_string_append_printf (string, "\"%s\" ", name); + _gtk_css_value_print (val, string); + } +} + +static const GtkCssValueClass GTK_CSS_VALUE_FONT_FEATURES = { + gtk_css_value_font_features_free, + gtk_css_value_font_features_compute, + gtk_css_value_font_features_equal, + gtk_css_value_font_features_transition, + gtk_css_value_font_features_print +}; + +static GtkCssValue * +gtk_css_font_features_value_new_empty (void) +{ + GtkCssValue *result; + + result = _gtk_css_value_new (GtkCssValue, >K_CSS_VALUE_FONT_FEATURES); + result->features = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, + (GDestroyNotify) _gtk_css_value_unref); + + return result; +} + +GtkCssValue * +gtk_css_font_features_value_new_default (void) +{ + if (default_font_features == NULL) + default_font_features = gtk_css_font_features_value_new_empty (); + + return _gtk_css_value_ref (default_font_features); +} + +static gboolean +is_valid_opentype_tag (const char *s) +{ + if (strlen (s) != 4) + return FALSE; + + if (s[0] < 0x20 || s[0] > 0x7e || + s[1] < 0x20 || s[1] > 0x7e || + s[2] < 0x20 || s[2] > 0x7e || + s[3] < 0x20 || s[3] > 0x7e) + return FALSE; + + return TRUE; +} + +GtkCssValue * +gtk_css_font_features_value_parse (GtkCssParser *parser) +{ + GtkCssValue *result, *val; + char *name; + int num; + + if (_gtk_css_parser_try (parser, "normal", TRUE)) + return gtk_css_font_features_value_new_default (); + + result = gtk_css_font_features_value_new_empty (); + + do { + _gtk_css_parser_skip_whitespace (parser); + name = _gtk_css_parser_read_string (parser); + if (name == NULL) + { + _gtk_css_value_unref (result); + return NULL; + } + + if (!is_valid_opentype_tag (name)) + { + _gtk_css_parser_error (parser, "Not a valid OpenType tag."); + g_free (name); + _gtk_css_value_unref (result); + return NULL; + } + + if (_gtk_css_parser_try (parser, "on", TRUE)) + val = _gtk_css_number_value_new (1.0, GTK_CSS_NUMBER); + else if (_gtk_css_parser_try (parser, "off", TRUE)) + val = _gtk_css_number_value_new (0.0, GTK_CSS_NUMBER); + else if (_gtk_css_parser_try_int (parser, &num)) + val = _gtk_css_number_value_new ((double)num, GTK_CSS_NUMBER); + else + val = _gtk_css_number_value_new (1.0, GTK_CSS_NUMBER); + + gtk_css_font_features_value_add_feature (result, name, val); + g_free (name); + } while (_gtk_css_parser_try (parser, ",", TRUE)); + + return result; +} + +char * +gtk_css_font_features_value_get_features (GtkCssValue *value) +{ + GtkCssValue *val; + GHashTableIter iter; + gboolean first = TRUE; + const char *name; + GString *string; + + g_return_val_if_fail (value->class == >K_CSS_VALUE_FONT_FEATURES, NULL); + + if (value == default_font_features) + return NULL; + + string = g_string_new (""); + + g_hash_table_iter_init (&iter, value->features); + while (g_hash_table_iter_next (&iter, (gpointer *)&name, (gpointer *)&val)) + { + if (first) + first = FALSE; + else + g_string_append (string, ", "); + g_string_append_printf (string, "%s %d", name, (int)_gtk_css_number_value_get (val, 100)); + } + + return g_string_free (string, FALSE); +} diff --git a/gtk/gtkcssfontfeaturesvalueprivate.h b/gtk/gtkcssfontfeaturesvalueprivate.h new file mode 100644 index 0000000000..834a9a09bd --- /dev/null +++ b/gtk/gtkcssfontfeaturesvalueprivate.h @@ -0,0 +1,36 @@ +/* + * Copyright © 2017 Red Hat Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Authors: Matthias Clasen + */ + +#ifndef __GTK_CSS_FONT_FEATURES_VALUE_PRIVATE_H__ +#define __GTK_CSS_FONT_FEATURES_PRIVATE_H__ + +#include "gtkcssparserprivate.h" +#include "gtkcssvalueprivate.h" + +G_BEGIN_DECLS + +GtkCssValue * gtk_css_font_features_value_new_default (void); + +GtkCssValue * gtk_css_font_features_value_parse (GtkCssParser *parser); + +char * gtk_css_font_features_value_get_features (GtkCssValue *value); + +G_END_DECLS + +#endif /* __GTK_CSS_FONT_FEATURES_VALUE_PRIVATE_H__ */ diff --git a/gtk/gtkcssstyle.c b/gtk/gtkcssstyle.c index 670a87d7b0..78af3ae9d4 100644 --- a/gtk/gtkcssstyle.c +++ b/gtk/gtkcssstyle.c @@ -32,6 +32,7 @@ #include "gtkcsssectionprivate.h" #include "gtkcssshorthandpropertyprivate.h" #include "gtkcssstringvalueprivate.h" +#include "gtkcssfontfeaturesvalueprivate.h" #include "gtkcssstylepropertyprivate.h" #include "gtkcsstransitionprivate.h" #include "gtkstyleanimationprivate.h" @@ -238,6 +239,7 @@ gtk_css_style_get_pango_attributes (GtkCssStyle *style) GtkCssFontVariantNumeric numeric; GtkCssFontVariantEastAsian east_asian; GString *s; + char *settings; /* text-decoration */ decoration_line = _gtk_css_text_decoration_line_value_get (gtk_css_style_get_value (style, GTK_CSS_PROPERTY_TEXT_DECORATION_LINE)); @@ -424,6 +426,16 @@ gtk_css_style_get_pango_attributes (GtkCssStyle *style) append_separated (s, "ruby 1"); } +g_print ("before: %s\n", s->str); + value = gtk_css_style_get_value (style, GTK_CSS_PROPERTY_FONT_FEATURE_SETTINGS); + settings = gtk_css_font_features_value_get_features (value); + if (settings) + { + append_separated (s, settings); + g_free (settings); + } +g_print ("after: %s\n", s->str); + attrs = add_pango_attr (attrs, pango_attr_font_features_new (s->str)); g_string_free (s, TRUE); diff --git a/gtk/gtkcssstylepropertyimpl.c b/gtk/gtkcssstylepropertyimpl.c index eacf945ab0..65c27d8ca5 100644 --- a/gtk/gtkcssstylepropertyimpl.c +++ b/gtk/gtkcssstylepropertyimpl.c @@ -657,6 +657,12 @@ parse_font_variant_east_asian (GtkCssStyleProperty *property, } static GtkCssValue * +parse_font_feature_settings (GtkCssStyleProperty *property, + GtkCssParser *parser) +{ + return gtk_css_font_features_value_parse (parser); +} + box_shadow_value_parse (GtkCssStyleProperty *property, GtkCssParser *parser) { @@ -1800,4 +1806,12 @@ _gtk_css_style_property_init_properties (void) color_parse, color_query, _gtk_css_color_value_new_current_color ()); + gtk_css_style_property_register ("font-feature-settings", + GTK_CSS_PROPERTY_FONT_FEATURE_SETTINGS, + G_TYPE_NONE, + GTK_STYLE_PROPERTY_INHERIT | GTK_STYLE_PROPERTY_ANIMATED, + GTK_CSS_AFFECTS_TEXT_ATTRS | GTK_CSS_AFFECTS_TEXT_SIZE, + parse_font_feature_settings, + NULL, + gtk_css_font_features_value_new_default ()); } diff --git a/gtk/gtkcsstypesprivate.h b/gtk/gtkcsstypesprivate.h index 0b73d106dd..f7b68ee445 100644 --- a/gtk/gtkcsstypesprivate.h +++ b/gtk/gtkcsstypesprivate.h @@ -244,6 +244,7 @@ enum { /*< skip >*/ GTK_CSS_PROPERTY_GTK_KEY_BINDINGS, GTK_CSS_PROPERTY_CARET_COLOR, GTK_CSS_PROPERTY_SECONDARY_CARET_COLOR, + GTK_CSS_PROPERTY_FONT_FEATURE_SETTINGS, /* add more */ GTK_CSS_PROPERTY_N_PROPERTIES }; diff --git a/gtk/meson.build b/gtk/meson.build index be8cc45df3..4b18f532fa 100644 --- a/gtk/meson.build +++ b/gtk/meson.build @@ -85,6 +85,7 @@ gtk_public_sources = files([ 'gtkcsseasevalue.c', 'gtkcssenumvalue.c', 'gtkcssfiltervalue.c', + 'gtkcssfontfeaturesvalue.c', 'gtkcssiconthemevalue.c', 'gtkcssimage.c', 'gtkcssimagebuiltin.c',