/* 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, int num) { g_hash_table_insert (value->features, g_strdup (name), GINT_TO_POINTER (num)); } 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) { return _gtk_css_value_ref (specified); } 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 (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; gpointer start_val, end_val; GHashTableIter iter; gpointer transition; GtkCssValue *result; /* 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 = start_val; else transition = progress > 0.5 ? start_val : end_val; gtk_css_font_features_value_add_feature (result, name, GPOINTER_TO_INT (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, GPOINTER_TO_INT (end_val)); } return result; } static void gtk_css_value_font_features_print (const GtkCssValue *value, GString *string) { GHashTableIter iter; const char *name; gpointer 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); g_string_append_printf (string, "%d", GPOINTER_TO_INT (val)); } } static const GtkCssValueClass GTK_CSS_VALUE_FONT_FEATURES = { "GtkCssFontFeaturesValue", gtk_css_value_font_features_free, gtk_css_value_font_features_compute, gtk_css_value_font_features_equal, gtk_css_value_font_features_transition, NULL, NULL, 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, NULL); result->is_computed = TRUE; 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; char *name; int num; if (gtk_css_parser_try_ident (parser, "normal")) return gtk_css_font_features_value_new_default (); result = gtk_css_font_features_value_new_empty (); do { name = gtk_css_parser_consume_string (parser); if (name == NULL) { _gtk_css_value_unref (result); return NULL; } if (!is_valid_opentype_tag (name)) { gtk_css_parser_error_value (parser, "Not a valid OpenType tag."); g_free (name); _gtk_css_value_unref (result); return NULL; } if (gtk_css_parser_try_ident (parser, "on")) num = 1; else if (gtk_css_parser_try_ident (parser, "off")) num = 0; else if (gtk_css_parser_has_integer (parser)) { if (!gtk_css_parser_consume_integer (parser, &num)) { g_free (name); _gtk_css_value_unref (result); return NULL; } } else num = 1; gtk_css_font_features_value_add_feature (result, name, num); g_free (name); } while (gtk_css_parser_try_token (parser, GTK_CSS_TOKEN_COMMA)); 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, GPOINTER_TO_INT (val)); } return g_string_free (string, FALSE); }