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 <gtk/css/gtkcss.h>
23 #include "gtk/css/gtkcsstokenizerprivate.h"
24 #include "gtk/css/gtkcssparserprivate.h"
25 #include "gtkcssnumbervalueprivate.h"
26 #include "gtkcssfontvariationsvalueprivate.h"
27
28 struct _GtkCssValue {
29 GTK_CSS_VALUE_BASE
30 GHashTable *axes;
31 };
32
33 static GtkCssValue *default_font_variations;
34
35 static GtkCssValue *gtk_css_font_variations_value_new_empty (void);
36
37 static void
gtk_css_font_variations_value_add_axis(GtkCssValue * value,const char * name,GtkCssValue * coord)38 gtk_css_font_variations_value_add_axis (GtkCssValue *value,
39 const char *name,
40 GtkCssValue *coord)
41 {
42 g_hash_table_insert (value->axes, g_strdup (name), coord);
43 }
44
45
46 static void
gtk_css_value_font_variations_free(GtkCssValue * value)47 gtk_css_value_font_variations_free (GtkCssValue *value)
48 {
49 g_hash_table_unref (value->axes);
50
51 g_slice_free (GtkCssValue, value);
52 }
53
54 static GtkCssValue *
gtk_css_value_font_variations_compute(GtkCssValue * specified,guint property_id,GtkStyleProvider * provider,GtkCssStyle * style,GtkCssStyle * parent_style)55 gtk_css_value_font_variations_compute (GtkCssValue *specified,
56 guint property_id,
57 GtkStyleProvider *provider,
58 GtkCssStyle *style,
59 GtkCssStyle *parent_style)
60 {
61 return _gtk_css_value_ref (specified);
62 }
63
64 static gboolean
gtk_css_value_font_variations_equal(const GtkCssValue * value1,const GtkCssValue * value2)65 gtk_css_value_font_variations_equal (const GtkCssValue *value1,
66 const GtkCssValue *value2)
67 {
68 gpointer name, coord1, coord2;
69 GHashTableIter iter;
70
71 if (g_hash_table_size (value1->axes) != g_hash_table_size (value2->axes))
72 return FALSE;
73
74 g_hash_table_iter_init (&iter, value1->axes);
75 while (g_hash_table_iter_next (&iter, &name, &coord1))
76 {
77 coord2 = g_hash_table_lookup (value2->axes, name);
78 if (coord2 == NULL)
79 return FALSE;
80
81 if (!_gtk_css_value_equal (coord1, coord2))
82 return FALSE;
83 }
84
85 return TRUE;
86 }
87
88 static GtkCssValue *
gtk_css_value_font_variations_transition(GtkCssValue * start,GtkCssValue * end,guint property_id,double progress)89 gtk_css_value_font_variations_transition (GtkCssValue *start,
90 GtkCssValue *end,
91 guint property_id,
92 double progress)
93 {
94 const char *name;
95 GtkCssValue *start_coord, *end_coord;
96 GHashTableIter iter;
97 GtkCssValue *result, *transition;
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_variations_value_new_empty ();
105
106 g_hash_table_iter_init (&iter, start->axes);
107 while (g_hash_table_iter_next (&iter, (gpointer *)&name, (gpointer *)&start_coord))
108 {
109 end_coord = g_hash_table_lookup (end->axes, name);
110 if (end_coord == NULL)
111 transition = _gtk_css_value_ref (start_coord);
112 else
113 transition = _gtk_css_value_transition (start_coord, end_coord, property_id, progress);
114
115 gtk_css_font_variations_value_add_axis (result, name, transition);
116 }
117
118 g_hash_table_iter_init (&iter, end->axes);
119 while (g_hash_table_iter_next (&iter, (gpointer *)&name, (gpointer *)&end_coord))
120 {
121 start_coord = g_hash_table_lookup (start->axes, name);
122 if (start_coord != NULL)
123 continue;
124
125 gtk_css_font_variations_value_add_axis (result, name, _gtk_css_value_ref (end_coord));
126 }
127
128 return result;
129 }
130
131 static void
gtk_css_value_font_variations_print(const GtkCssValue * value,GString * string)132 gtk_css_value_font_variations_print (const GtkCssValue *value,
133 GString *string)
134 {
135 GHashTableIter iter;
136 const char *name;
137 GtkCssValue *coord;
138 gboolean first = TRUE;
139
140 if (value == default_font_variations)
141 {
142 g_string_append (string, "normal");
143 return;
144 }
145
146 g_hash_table_iter_init (&iter, value->axes);
147 while (g_hash_table_iter_next (&iter, (gpointer *)&name, (gpointer *)&coord))
148 {
149 if (first)
150 first = FALSE;
151 else
152 g_string_append (string, ", ");
153 g_string_append_printf (string, "\"%s\" ", name);
154 _gtk_css_value_print (coord, string);
155 }
156 }
157
158 static const GtkCssValueClass GTK_CSS_VALUE_FONT_VARIATIONS = {
159 "GtkCssFontVariationsValue",
160 gtk_css_value_font_variations_free,
161 gtk_css_value_font_variations_compute,
162 gtk_css_value_font_variations_equal,
163 gtk_css_value_font_variations_transition,
164 NULL,
165 NULL,
166 gtk_css_value_font_variations_print
167 };
168
169 static GtkCssValue *
gtk_css_font_variations_value_new_empty(void)170 gtk_css_font_variations_value_new_empty (void)
171 {
172 GtkCssValue *result;
173
174 result = _gtk_css_value_new (GtkCssValue, >K_CSS_VALUE_FONT_VARIATIONS);
175 result->axes = g_hash_table_new_full (g_str_hash, g_str_equal,
176 g_free,
177 (GDestroyNotify) _gtk_css_value_unref);
178 result->is_computed = TRUE;
179
180 return result;
181 }
182
183 GtkCssValue *
gtk_css_font_variations_value_new_default(void)184 gtk_css_font_variations_value_new_default (void)
185 {
186 if (default_font_variations == NULL)
187 default_font_variations = gtk_css_font_variations_value_new_empty ();
188
189 return _gtk_css_value_ref (default_font_variations);
190 }
191
192 static gboolean
is_valid_opentype_tag(const char * s)193 is_valid_opentype_tag (const char *s)
194 {
195 if (strlen (s) != 4)
196 return FALSE;
197
198 if (s[0] < 0x20 || s[0] > 0x7e ||
199 s[1] < 0x20 || s[1] > 0x7e ||
200 s[2] < 0x20 || s[2] > 0x7e ||
201 s[3] < 0x20 || s[3] > 0x7e)
202 return FALSE;
203
204 return TRUE;
205 }
206
207 GtkCssValue *
gtk_css_font_variations_value_parse(GtkCssParser * parser)208 gtk_css_font_variations_value_parse (GtkCssParser *parser)
209 {
210 GtkCssValue *result, *coord;
211 char *name;
212
213 if (gtk_css_parser_try_ident (parser, "normal"))
214 return gtk_css_font_variations_value_new_default ();
215
216 result = gtk_css_font_variations_value_new_empty ();
217
218 do {
219 name = gtk_css_parser_consume_string (parser);
220 if (name == NULL)
221 {
222 _gtk_css_value_unref (result);
223 return NULL;
224 }
225
226 if (!is_valid_opentype_tag (name))
227 {
228 gtk_css_parser_error_value (parser, "Not a valid OpenType tag.");
229 g_free (name);
230 _gtk_css_value_unref (result);
231 return NULL;
232 }
233
234 coord = _gtk_css_number_value_parse (parser, GTK_CSS_PARSE_NUMBER);
235 if (coord == NULL)
236 {
237 g_free (name);
238 _gtk_css_value_unref (result);
239 return NULL;
240 }
241
242 gtk_css_font_variations_value_add_axis (result, name, coord);
243 g_free (name);
244 } while (gtk_css_parser_try_token (parser, GTK_CSS_TOKEN_COMMA));
245
246 return result;
247 }
248
249 char *
gtk_css_font_variations_value_get_variations(GtkCssValue * value)250 gtk_css_font_variations_value_get_variations (GtkCssValue *value)
251 {
252 GtkCssValue *coord;
253 GHashTableIter iter;
254 gboolean first = TRUE;
255 const char *name;
256 GString *string;
257
258 g_return_val_if_fail (value->class == >K_CSS_VALUE_FONT_VARIATIONS, NULL);
259
260 if (value == default_font_variations)
261 return NULL;
262
263 string = g_string_new ("");
264
265 g_hash_table_iter_init (&iter, value->axes);
266 while (g_hash_table_iter_next (&iter, (gpointer *)&name, (gpointer *)&coord))
267 {
268 if (first)
269 first = FALSE;
270 else
271 g_string_append (string, ",");
272 g_string_append_printf (string, "%s=%g", name,
273 _gtk_css_number_value_get (coord, 100));
274 }
275
276 return g_string_free (string, FALSE);
277 }
278