1/* GTK - The GIMP Toolkit
2 * Copyright (C) 2017 Red Hat, Inc.
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16 *
17 * Author: Matthias Clasen
18 */
19
20#include "config.h"
21
22#include "gtkcsstypesprivate.h"
23#include <gtk/css/gtkcss.h>
24#include "gtk/css/gtkcsstokenizerprivate.h"
25#include "gtk/css/gtkcssparserprivate.h"
26#include "gtkcssnumbervalueprivate.h"
27#include "gtkcssfontfeaturesvalueprivate.h"
28
29struct _GtkCssValue {
30 GTK_CSS_VALUE_BASE
31 GHashTable *features;
32};
33
34static GtkCssValue *default_font_features;
35
36static GtkCssValue *gtk_css_font_features_value_new_empty (void);
37
38static void
39gtk_css_font_features_value_add_feature (GtkCssValue *value,
40 const char *name,
41 int num)
42{
43 g_hash_table_insert (hash_table: value->features, key: g_strdup (str: name), GINT_TO_POINTER (num));
44}
45
46
47static void
48gtk_css_value_font_features_free (GtkCssValue *value)
49{
50 g_hash_table_unref (hash_table: value->features);
51
52 g_slice_free (GtkCssValue, value);
53}
54
55static GtkCssValue *
56gtk_css_value_font_features_compute (GtkCssValue *specified,
57 guint property_id,
58 GtkStyleProvider *provider,
59 GtkCssStyle *style,
60 GtkCssStyle *parent_style)
61{
62 return _gtk_css_value_ref (value: specified);
63}
64
65static gboolean
66gtk_css_value_font_features_equal (const GtkCssValue *value1,
67 const GtkCssValue *value2)
68{
69 gpointer name, val1, val2;
70 GHashTableIter iter;
71
72 if (g_hash_table_size (hash_table: value1->features) != g_hash_table_size (hash_table: value2->features))
73 return FALSE;
74
75 g_hash_table_iter_init (iter: &iter, hash_table: value1->features);
76 while (g_hash_table_iter_next (iter: &iter, key: &name, value: &val1))
77 {
78 val2 = g_hash_table_lookup (hash_table: value2->features, key: name);
79
80 if (val1 != val2)
81 return FALSE;
82 }
83
84 return TRUE;
85}
86
87static GtkCssValue *
88gtk_css_value_font_features_transition (GtkCssValue *start,
89 GtkCssValue *end,
90 guint property_id,
91 double progress)
92{
93 const char *name;
94 gpointer start_val, end_val;
95 GHashTableIter iter;
96 gpointer transition;
97 GtkCssValue *result;
98
99 /* XXX: For value that are only in start or end but not both,
100 * we don't transition but just keep the value.
101 * That causes an abrupt transition to the new value at the end.
102 */
103
104 result = gtk_css_font_features_value_new_empty ();
105
106 g_hash_table_iter_init (iter: &iter, hash_table: start->features);
107 while (g_hash_table_iter_next (iter: &iter, key: (gpointer *)&name, value: (gpointer *)&start_val))
108 {
109 end_val = g_hash_table_lookup (hash_table: end->features, key: name);
110 if (end_val == NULL)
111 transition = start_val;
112 else
113 transition = progress > 0.5 ? start_val : end_val;
114
115 gtk_css_font_features_value_add_feature (value: result, name, GPOINTER_TO_INT (transition));
116 }
117
118 g_hash_table_iter_init (iter: &iter, hash_table: end->features);
119 while (g_hash_table_iter_next (iter: &iter, key: (gpointer *)&name, value: (gpointer *)&end_val))
120 {
121 start_val = g_hash_table_lookup (hash_table: start->features, key: name);
122 if (start_val != NULL)
123 continue;
124
125 gtk_css_font_features_value_add_feature (value: result, name, GPOINTER_TO_INT (end_val));
126 }
127
128 return result;
129}
130
131static void
132gtk_css_value_font_features_print (const GtkCssValue *value,
133 GString *string)
134{
135 GHashTableIter iter;
136 const char *name;
137 gpointer val;
138 gboolean first = TRUE;
139
140 if (value == default_font_features)
141 {
142 g_string_append (string, val: "normal");
143 return;
144 }
145
146 g_hash_table_iter_init (iter: &iter, hash_table: value->features);
147 while (g_hash_table_iter_next (iter: &iter, key: (gpointer *)&name, value: (gpointer *)&val))
148 {
149 if (first)
150 first = FALSE;
151 else
152 g_string_append (string, val: ", ");
153 g_string_append_printf (string, format: "\"%s\" ", name);
154 g_string_append_printf (string, format: "%d", GPOINTER_TO_INT (val));
155 }
156}
157
158static const GtkCssValueClass GTK_CSS_VALUE_FONT_FEATURES = {
159 "GtkCssFontFeaturesValue",
160 gtk_css_value_font_features_free,
161 gtk_css_value_font_features_compute,
162 gtk_css_value_font_features_equal,
163 gtk_css_value_font_features_transition,
164 NULL,
165 NULL,
166 gtk_css_value_font_features_print
167};
168
169static GtkCssValue *
170gtk_css_font_features_value_new_empty (void)
171{
172 GtkCssValue *result;
173
174 result = _gtk_css_value_new (GtkCssValue, &GTK_CSS_VALUE_FONT_FEATURES);
175 result->features = g_hash_table_new_full (hash_func: g_str_hash, key_equal_func: g_str_equal, key_destroy_func: g_free, NULL);
176 result->is_computed = TRUE;
177
178 return result;
179}
180
181GtkCssValue *
182gtk_css_font_features_value_new_default (void)
183{
184 if (default_font_features == NULL)
185 default_font_features = gtk_css_font_features_value_new_empty ();
186
187 return _gtk_css_value_ref (value: default_font_features);
188}
189
190static gboolean
191is_valid_opentype_tag (const char *s)
192{
193 if (strlen (s: s) != 4)
194 return FALSE;
195
196 if (s[0] < 0x20 || s[0] > 0x7e ||
197 s[1] < 0x20 || s[1] > 0x7e ||
198 s[2] < 0x20 || s[2] > 0x7e ||
199 s[3] < 0x20 || s[3] > 0x7e)
200 return FALSE;
201
202 return TRUE;
203}
204
205GtkCssValue *
206gtk_css_font_features_value_parse (GtkCssParser *parser)
207{
208 GtkCssValue *result;
209 char *name;
210 int num;
211
212 if (gtk_css_parser_try_ident (self: parser, ident: "normal"))
213 return gtk_css_font_features_value_new_default ();
214
215 result = gtk_css_font_features_value_new_empty ();
216
217 do {
218 name = gtk_css_parser_consume_string (self: parser);
219 if (name == NULL)
220 {
221 _gtk_css_value_unref (value: result);
222 return NULL;
223 }
224
225 if (!is_valid_opentype_tag (s: name))
226 {
227 gtk_css_parser_error_value (self: parser, format: "Not a valid OpenType tag.");
228 g_free (mem: name);
229 _gtk_css_value_unref (value: result);
230 return NULL;
231 }
232
233 if (gtk_css_parser_try_ident (self: parser, ident: "on"))
234 num = 1;
235 else if (gtk_css_parser_try_ident (self: parser, ident: "off"))
236 num = 0;
237 else if (gtk_css_parser_has_integer (self: parser))
238 {
239 if (!gtk_css_parser_consume_integer (self: parser, number: &num))
240 {
241 g_free (mem: name);
242 _gtk_css_value_unref (value: result);
243 return NULL;
244 }
245 }
246 else
247 num = 1;
248
249 gtk_css_font_features_value_add_feature (value: result, name, num);
250 g_free (mem: name);
251 } while (gtk_css_parser_try_token (self: parser, token_type: GTK_CSS_TOKEN_COMMA));
252
253 return result;
254}
255
256char *
257gtk_css_font_features_value_get_features (GtkCssValue *value)
258{
259 GtkCssValue *val;
260 GHashTableIter iter;
261 gboolean first = TRUE;
262 const char *name;
263 GString *string;
264
265 g_return_val_if_fail (value->class == &GTK_CSS_VALUE_FONT_FEATURES, NULL);
266
267 if (value == default_font_features)
268 return NULL;
269
270 string = g_string_new (init: "");
271
272 g_hash_table_iter_init (iter: &iter, hash_table: value->features);
273 while (g_hash_table_iter_next (iter: &iter, key: (gpointer *)&name, value: (gpointer *)&val))
274 {
275 if (first)
276 first = FALSE;
277 else
278 g_string_append (string, val: ", ");
279 g_string_append_printf (string, format: "%s %d", name, GPOINTER_TO_INT (val));
280 }
281
282 return g_string_free (string, FALSE);
283}
284

source code of gtk/gtk/gtkcssfontfeaturesvalue.c