1/* Pango
2 *
3 * Copyright (C) 2021 Matthias Clasen
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
14 *
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
19 */
20
21#include "config.h"
22
23#include <glib.h>
24#include <pango/pangocairo.h>
25#include <pango/pangocairo-fc.h>
26#include <gio/gio.h>
27
28static void
29test_serialize_attr_list (void)
30{
31 const char *valid[] = {
32 "5 16 style italic",
33 "0 10 foreground red, 5 15 weight bold, 0 200 font-desc \"Sans Small-Caps 10\"",
34 "0 10 foreground red\n5 15 weight bold\n0 200 font-desc \"Sans Small-Caps 10\"",
35 " 0 10 fallback false,\n 5 15 weight semilight\n\n \n \n",
36 "0 100 font-desc \"Cantarell, Sans, Italic Ultra-Light 64\", 10 11 weight 100",
37 "0 -1 size 10",
38 "0 1 weight 700, 2 4 weight book",
39 "0 200 rise 100\n5 15 family Times\n10 11 size 10240\n11 100 fallback 0\n30 60 stretch 2\n",
40 ""
41 };
42 const char *roundtripped[] = {
43 "5 16 style italic",
44 "0 10 foreground #ffff00000000\n5 15 weight bold\n0 200 font-desc \"Sans Small-Caps 10\"",
45 "0 10 foreground #ffff00000000\n5 15 weight bold\n0 200 font-desc \"Sans Small-Caps 10\"",
46 "0 10 fallback false\n5 15 weight semilight",
47 "0 100 font-desc \"Cantarell,Sans Ultra-Light Italic 64\"\n10 11 weight thin",
48 "0 4294967295 size 10",
49 "0 1 weight bold\n2 4 weight book",
50 "0 200 rise 100\n5 15 family Times\n10 11 size 10240\n11 100 fallback false\n30 60 stretch condensed",
51 ""
52 };
53 const char *invalid[] = {
54 "not an attr list",
55 "0 -1 FOREGROUND xyz",
56 ",,bla.wewq",
57 };
58
59 for (int i = 0; i < G_N_ELEMENTS (valid); i++)
60 {
61 PangoAttrList *attrs;
62 char *str;
63
64 attrs = pango_attr_list_from_string (text: valid[i]);
65 g_assert_nonnull (attrs);
66 str = pango_attr_list_to_string (list: attrs);
67 g_assert_cmpstr (str, ==, roundtripped[i]);
68 g_free (mem: str);
69 pango_attr_list_unref (list: attrs);
70 }
71
72 for (int i = 0; i < G_N_ELEMENTS (invalid); i++)
73 {
74 PangoAttrList *attrs;
75
76 attrs = pango_attr_list_from_string (text: invalid[i]);
77 g_assert_null (attrs);
78 }
79}
80
81static void
82test_serialize_tab_array (void)
83{
84 const char *valid[] = {
85 "0 10 100 200 400",
86 "0px 10px 100px 200px 400px",
87 " 0 10 ",
88 "20 10",
89 "left:10px right:20px center:30px decimal:40px",
90 "decimal:10240:94",
91 ""
92 };
93 const char *roundtripped[] = {
94 "0\n10\n100\n200\n400",
95 "0px\n10px\n100px\n200px\n400px",
96 "0\n10",
97 "20\n10",
98 "10px\nright:20px\ncenter:30px\ndecimal:40px",
99 "decimal:10240:94",
100 ""
101 };
102 const char *invalid[] = {
103 "not a tabarray",
104 "-10:-20",
105 "10ps 20pu",
106 "10, 20",
107 "10 20px 30",
108 };
109
110 for (int i = 0; i < G_N_ELEMENTS (valid); i++)
111 {
112 PangoTabArray *tabs;
113 char *str;
114
115 tabs = pango_tab_array_from_string (text: valid[i]);
116 g_assert_nonnull (tabs);
117 str = pango_tab_array_to_string (tab_array: tabs);
118 g_assert_cmpstr (str, ==, roundtripped[i]);
119 g_free (mem: str);
120 pango_tab_array_free (tab_array: tabs);
121 }
122
123 for (int i = 0; i < G_N_ELEMENTS (invalid); i++)
124 {
125 PangoTabArray *tabs;
126
127 tabs = pango_tab_array_from_string (text: invalid[i]);
128 g_assert_null (tabs);
129 }
130}
131
132static void
133test_serialize_font (void)
134{
135 PangoContext *context;
136 PangoFontDescription *desc;
137 PangoFont *font;
138 PangoFont *font2;
139 GBytes *bytes;
140 GBytes *bytes2;
141 GError *error = NULL;
142 const char *expected =
143 "{\n"
144 " \"description\" : \"Cantarell 20 @wght=600\",\n"
145 " \"checksum\" : \"5bcb6ee14ee9d210b2e91d643de1fe456e9d1aea770983fdb05951545efebbe2\",\n"
146 " \"variations\" : {\n"
147 " \"wght\" : 5583\n"
148 " },\n"
149 " \"matrix\" : [\n"
150 " 1,\n"
151 " -0,\n"
152 " -0,\n"
153 " 1,\n"
154 " 0,\n"
155 " 0\n"
156 " ]\n"
157 "}";
158
159 context = pango_font_map_create_context (fontmap: pango_cairo_font_map_get_default ());
160 desc = pango_font_description_from_string (str: "Cantarell Italic 20 @wght=600");
161 font = pango_context_load_font (context, desc);
162
163 bytes = pango_font_serialize (font);
164 g_assert_cmpstr (g_bytes_get_data (bytes, NULL), ==, expected);
165
166 font2 = pango_font_deserialize (context, bytes, error: &error);
167 g_assert_no_error (error);
168 g_assert_nonnull (font2);
169
170 bytes2 = pango_font_serialize (font: font2);
171 g_assert_true (g_bytes_equal (bytes2, bytes));
172
173 g_object_unref (object: font);
174 g_object_unref (object: font2);
175 pango_font_description_free (desc);
176
177 g_bytes_unref (bytes);
178 g_bytes_unref (bytes: bytes2);
179 g_object_unref (object: context);
180}
181
182static void
183test_serialize_layout_minimal (void)
184{
185 const char *test =
186 "{\n"
187 " \"text\" : \"Almost nothing\"\n"
188 "}\n";
189
190 PangoContext *context;
191 GBytes *bytes;
192 PangoLayout *layout;
193 GError *error = NULL;
194 GBytes *out_bytes;
195 const char *str;
196
197 context = pango_font_map_create_context (fontmap: pango_cairo_font_map_get_default ());
198
199 bytes = g_bytes_new_static (data: test, size: strlen (s: test) + 1);
200
201 layout = pango_layout_deserialize (context, bytes, flags: PANGO_LAYOUT_DESERIALIZE_DEFAULT, error: &error);
202 g_assert_no_error (error);
203 g_assert_true (PANGO_IS_LAYOUT (layout));
204 g_assert_cmpstr (pango_layout_get_text (layout), ==, "Almost nothing");
205 g_assert_null (pango_layout_get_attributes (layout));
206 g_assert_null (pango_layout_get_tabs (layout));
207 g_assert_null (pango_layout_get_font_description (layout));
208 g_assert_cmpint (pango_layout_get_alignment (layout), ==, PANGO_ALIGN_LEFT);
209 g_assert_cmpint (pango_layout_get_width (layout), ==, -1);
210
211 out_bytes = pango_layout_serialize (layout, flags: PANGO_LAYOUT_SERIALIZE_DEFAULT);
212 str = g_bytes_get_data (bytes: out_bytes, NULL);
213
214 g_assert_cmpstr (str, ==, test);
215
216 g_bytes_unref (bytes: out_bytes);
217
218 g_object_unref (object: layout);
219 g_bytes_unref (bytes);
220 g_object_unref (object: context);
221}
222
223static void
224test_serialize_layout_valid (void)
225{
226 const char *test =
227 "{\n"
228 " \"text\" : \"Some fun with layouts!\",\n"
229 " \"attributes\" : [\n"
230 " {\n"
231 " \"end\" : 4,\n"
232 " \"type\" : \"foreground\",\n"
233 " \"value\" : \"#ffff00000000\"\n"
234 " },\n"
235 " {\n"
236 " \"start\" : 5,\n"
237 " \"end\" : 8,\n"
238 " \"type\" : \"foreground\",\n"
239 " \"value\" : \"#000080800000\"\n"
240 " },\n"
241 " {\n"
242 " \"start\" : 14,\n"
243 " \"type\" : \"foreground\",\n"
244 " \"value\" : \"#ffff0000ffff\"\n"
245 " }\n"
246 " ],\n"
247 " \"font\" : \"Sans Bold 32\",\n"
248 " \"tabs\" : {\n"
249 " \"positions-in-pixels\" : true,\n"
250 " \"positions\" : [\n"
251 " {\n"
252 " \"position\" : 0,\n"
253 " \"alignment\" : \"left\",\n"
254 " \"decimal-point\" : 0\n"
255 " },\n"
256 " {\n"
257 " \"position\" : 50,\n"
258 " \"alignment\" : \"center\",\n"
259 " \"decimal-point\" : 0\n"
260 " },\n"
261 " {\n"
262 " \"position\" : 100,\n"
263 " \"alignment\" : \"decimal\",\n"
264 " \"decimal-point\" : 94\n"
265 " }\n"
266 " ]\n"
267 " },\n"
268 " \"alignment\" : \"center\",\n"
269 " \"width\" : 350000,\n"
270 " \"line-spacing\" : 1.5\n"
271 "}\n";
272
273 PangoContext *context;
274 GBytes *bytes;
275 PangoLayout *layout;
276 PangoTabArray *tabs;
277 GError *error = NULL;
278 GBytes *out_bytes;
279 char *s;
280
281 context = pango_font_map_create_context (fontmap: pango_cairo_font_map_get_default ());
282
283 bytes = g_bytes_new_static (data: test, size: strlen (s: test) + 1);
284
285 layout = pango_layout_deserialize (context, bytes, flags: PANGO_LAYOUT_DESERIALIZE_DEFAULT, error: &error);
286 g_assert_no_error (error);
287 g_assert_true (PANGO_IS_LAYOUT (layout));
288 g_assert_cmpstr (pango_layout_get_text (layout), ==, "Some fun with layouts!");
289 g_assert_nonnull (pango_layout_get_attributes (layout));
290 tabs = pango_layout_get_tabs (layout);
291 g_assert_nonnull (tabs);
292 pango_tab_array_free (tab_array: tabs);
293 s = pango_font_description_to_string (desc: pango_layout_get_font_description (layout));
294 g_assert_cmpstr (s, ==, "Sans Bold 32");
295 g_free (mem: s);
296 g_assert_cmpint (pango_layout_get_alignment (layout), ==, PANGO_ALIGN_CENTER);
297 g_assert_cmpint (pango_layout_get_width (layout), ==, 350000);
298 g_assert_cmpfloat_with_epsilon (pango_layout_get_line_spacing (layout), 1.5, 0.0001);
299
300 out_bytes = pango_layout_serialize (layout, flags: PANGO_LAYOUT_SERIALIZE_DEFAULT);
301
302 if (strcmp (s1: g_bytes_get_data (bytes: out_bytes, NULL), s2: g_bytes_get_data (bytes, NULL)) != 0)
303 {
304 g_print (format: "expected:\n%s\ngot:\n%s\n",
305 (char *)g_bytes_get_data (bytes, NULL),
306 (char *)g_bytes_get_data (bytes: out_bytes, NULL));
307 }
308
309 g_assert_cmpstr (g_bytes_get_data (out_bytes, NULL), ==, g_bytes_get_data (bytes, NULL));
310
311 g_bytes_unref (bytes: out_bytes);
312 g_bytes_unref (bytes);
313
314 g_object_unref (object: layout);
315 g_object_unref (object: context);
316}
317
318static void
319test_serialize_layout_context (void)
320{
321 const char *test =
322 "{\n"
323 " \"context\" : {\n"
324 " \"base-gravity\" : \"east\",\n"
325 " \"language\" : \"de-de\",\n"
326 " \"round-glyph-positions\" : false\n"
327 " },\n"
328 " \"text\" : \"Some fun with layouts!\"\n"
329 "}\n";
330
331 PangoContext *context;
332 GBytes *bytes;
333 PangoLayout *layout;
334 GError *error = NULL;
335
336 context = pango_font_map_create_context (fontmap: pango_cairo_font_map_get_default ());
337
338 bytes = g_bytes_new_static (data: test, size: strlen (s: test) + 1);
339
340 layout = pango_layout_deserialize (context, bytes, flags: PANGO_LAYOUT_DESERIALIZE_CONTEXT, error: &error);
341 g_assert_no_error (error);
342 g_assert_true (PANGO_IS_LAYOUT (layout));
343 g_assert_cmpstr (pango_layout_get_text (layout), ==, "Some fun with layouts!");
344
345 g_assert_cmpint (pango_context_get_base_gravity (context), ==, PANGO_GRAVITY_EAST);
346 g_assert_true (pango_context_get_language (context) == pango_language_from_string ("de-de"));
347 g_assert_false (pango_context_get_round_glyph_positions (context));
348
349 g_object_unref (object: layout);
350 g_bytes_unref (bytes);
351 g_object_unref (object: context);
352}
353
354static void
355test_serialize_layout_invalid (void)
356{
357 struct {
358 const char *json;
359 int expected_error;
360 } test[] = {
361 {
362 "{\n"
363 " \"attributes\" : [\n"
364 " {\n"
365 " \"type\" : \"caramba\"\n"
366 " }\n"
367 " ]\n"
368 "}\n",
369 PANGO_LAYOUT_DESERIALIZE_INVALID_VALUE
370 },
371 {
372 "{\n"
373 " \"attributes\" : [\n"
374 " {\n"
375 " \"type\" : \"weight\"\n"
376 " }\n"
377 " ]\n"
378 "}\n",
379 PANGO_LAYOUT_DESERIALIZE_MISSING_VALUE
380 },
381 {
382 "{\n"
383 " \"attributes\" : [\n"
384 " {\n"
385 " \"type\" : \"alignment\",\n"
386 " \"value\" : \"nonsense\"\n"
387 " }\n"
388 " ]\n"
389 "}\n",
390 PANGO_LAYOUT_DESERIALIZE_INVALID_VALUE
391 },
392 {
393 "{\n"
394 " \"alignment\" : \"nonsense\"\n"
395 "}\n",
396 PANGO_LAYOUT_DESERIALIZE_INVALID_VALUE
397 },
398 {
399 "{\n"
400 " \"attributes\" : {\n"
401 " \"name\" : \"This is wrong\"\n"
402 " }\n"
403 "}\n",
404 0,
405 }
406 };
407
408 PangoContext *context;
409
410 context = pango_font_map_create_context (fontmap: pango_cairo_font_map_get_default ());
411
412 for (int i = 0; i < G_N_ELEMENTS (test); i++)
413 {
414 GBytes *bytes;
415 PangoLayout *layout;
416 GError *error = NULL;
417
418 bytes = g_bytes_new_static (data: test[i].json, size: strlen (s: test[i].json) + 1);
419 layout = pango_layout_deserialize (context, bytes, flags: PANGO_LAYOUT_DESERIALIZE_DEFAULT, error: &error);
420 g_assert_null (layout);
421 if (test[i].expected_error)
422 g_assert_error (error, PANGO_LAYOUT_DESERIALIZE_ERROR, test[i].expected_error);
423 else
424 g_assert_nonnull (error);
425 g_bytes_unref (bytes);
426 g_clear_error (err: &error);
427 }
428
429 g_object_unref (object: context);
430}
431
432static void
433install_fonts (void)
434{
435 char *dir;
436 FcConfig *config;
437 PangoFontMap *map;
438 char *path;
439 gsize len;
440 char *conf;
441
442 dir = g_test_build_filename (file_type: G_TEST_DIST, first_path: "fonts", NULL);
443
444 map = g_object_new (PANGO_TYPE_CAIRO_FC_FONT_MAP, NULL);
445
446 config = FcConfigCreate ();
447
448 path = g_test_build_filename (file_type: G_TEST_DIST, first_path: "fonts/fonts.conf", NULL);
449 g_file_get_contents (filename: path, contents: &conf, length: &len, NULL);
450
451 if (!FcConfigParseAndLoadFromMemory (config, buffer: (const FcChar8 *) conf, TRUE))
452 g_error ("Failed to parse fontconfig configuration");
453
454 g_free (mem: conf);
455 g_free (mem: path);
456
457 FcConfigAppFontAddDir (config, dir: (const FcChar8 *) dir);
458 pango_fc_font_map_set_config (PANGO_FC_FONT_MAP (map), fcconfig: config);
459 FcConfigDestroy (config);
460
461 pango_cairo_font_map_set_default (PANGO_CAIRO_FONT_MAP (map));
462
463 g_object_unref (object: map);
464
465 g_free (mem: dir);
466}
467
468int
469main (int argc, char *argv[])
470{
471 g_test_init (argc: &argc, argv: &argv, NULL);
472
473 install_fonts ();
474
475 g_test_add_func (testpath: "/serialize/attr-list", test_func: test_serialize_attr_list);
476 g_test_add_func (testpath: "/serialize/tab-array", test_func: test_serialize_tab_array);
477 g_test_add_func (testpath: "/serialize/font", test_func: test_serialize_font);
478 g_test_add_func (testpath: "/serialize/layout/minimal", test_func: test_serialize_layout_minimal);
479 g_test_add_func (testpath: "/serialize/layout/valid", test_func: test_serialize_layout_valid);
480 g_test_add_func (testpath: "/serialize/layout/context", test_func: test_serialize_layout_context);
481 g_test_add_func (testpath: "/serialize/layout/invalid", test_func: test_serialize_layout_invalid);
482
483 return g_test_run ();
484}
485

source code of gtk/subprojects/pango/tests/testserialize.c