1/* Pango
2 * test-break.c: Test Pango line breaking
3 *
4 * Copyright (C) 2019 Red Hat, Inc
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Library General Public
17 * License along with this library; if not, write to the
18 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 * Boston, MA 02111-1307, USA.
20 */
21
22#include <glib.h>
23#include <string.h>
24#include <locale.h>
25
26#ifndef G_OS_WIN32
27#include <unistd.h>
28#endif
29
30#include "config.h"
31#include <pango/pangocairo.h>
32#include "test-common.h"
33
34#include "pango/pango-item-private.h"
35
36
37static PangoContext *context;
38
39static void
40append_text (GString *s,
41 const char *text,
42 int len)
43{
44 const char *p;
45
46 for (p = text; p < text + len; p = g_utf8_next_char (p))
47 {
48 gunichar ch = g_utf8_get_char (p);
49 if (ch == 0x0A || ch == 0x2028 || !g_unichar_isprint (c: ch))
50 g_string_append_printf (string: s, format: "[%#04x]", ch);
51 else
52 g_string_append_unichar (string: s, wc: ch);
53 }
54}
55
56static gboolean
57affects_itemization (PangoAttribute *attr,
58 gpointer data)
59{
60 switch ((int)attr->klass->type)
61 {
62 /* These affect font selection */
63 case PANGO_ATTR_LANGUAGE:
64 case PANGO_ATTR_FAMILY:
65 case PANGO_ATTR_STYLE:
66 case PANGO_ATTR_WEIGHT:
67 case PANGO_ATTR_VARIANT:
68 case PANGO_ATTR_STRETCH:
69 case PANGO_ATTR_SIZE:
70 case PANGO_ATTR_FONT_DESC:
71 case PANGO_ATTR_SCALE:
72 case PANGO_ATTR_FALLBACK:
73 case PANGO_ATTR_ABSOLUTE_SIZE:
74 case PANGO_ATTR_GRAVITY:
75 case PANGO_ATTR_GRAVITY_HINT:
76 case PANGO_ATTR_FONT_SCALE:
77 /* These are part of ItemProperties, so need to break runs */
78 case PANGO_ATTR_LETTER_SPACING:
79 case PANGO_ATTR_SHAPE:
80 case PANGO_ATTR_RISE:
81 case PANGO_ATTR_BASELINE_SHIFT:
82 case PANGO_ATTR_LINE_HEIGHT:
83 case PANGO_ATTR_ABSOLUTE_LINE_HEIGHT:
84 case PANGO_ATTR_TEXT_TRANSFORM:
85 return TRUE;
86 default:
87 return FALSE;
88 }
89}
90
91static void
92apply_attributes_to_items (GList *items,
93 PangoAttrList *attrs)
94{
95 GList *l;
96 PangoAttrIterator *iter;
97
98 if (!attrs)
99 return;
100
101 iter = pango_attr_list_get_iterator (list: attrs);
102
103 for (l = items; l; l = l->next)
104 {
105 PangoItem *item = l->data;
106 pango_item_apply_attrs (item, iter);
107 }
108
109 pango_attr_iterator_destroy (iterator: iter);
110}
111
112static int
113get_item_char_offset (PangoItem *item)
114{
115 if (item->analysis.flags & PANGO_ANALYSIS_FLAG_HAS_CHAR_OFFSET)
116 return ((PangoItemPrivate *)item)->char_offset;
117
118 return -1;
119}
120
121static void
122test_file (const gchar *filename, GString *string)
123{
124 gchar *contents;
125 gsize length;
126 GError *error = NULL;
127 GString *s1, *s2, *s3, *s4, *s5, *s6, *s7;
128 char *test;
129 char *text;
130 PangoAttrList *attrs;
131 PangoAttrList *itemize_attrs;
132 GList *items, *l;
133 const char *sep = "";
134
135 g_file_get_contents (filename, contents: &contents, length: &length, error: &error);
136 g_assert_no_error (error);
137
138 test = contents;
139
140 /* Skip initial comments */
141 while (test[0] == '#')
142 test = strchr (s: test, c: '\n') + 1;
143
144 pango_parse_markup (markup_text: test, length: -1, accel_marker: 0, attr_list: &attrs, text: &text, NULL, error: &error);
145 g_assert_no_error (error);
146
147 s1 = g_string_new (init: "Items: ");
148 s2 = g_string_new (init: "Font: ");
149 s3 = g_string_new (init: "Script: ");
150 s4 = g_string_new (init: "Lang: ");
151 s5 = g_string_new (init: "Bidi: ");
152 s6 = g_string_new (init: "Attrs: ");
153 s7 = g_string_new (init: "Chars: ");
154
155 length = strlen (s: text);
156 if (text[length - 1] == '\n')
157 length--;
158
159 itemize_attrs = pango_attr_list_filter (list: attrs, func: affects_itemization, NULL);
160 items = pango_itemize (context, text, start_index: 0, length, attrs: itemize_attrs, NULL);
161
162 apply_attributes_to_items (items, attrs);
163 pango_attr_list_unref (list: itemize_attrs);
164
165 for (l = items; l; l = l->next)
166 {
167 PangoItem *item = l->data;
168 PangoFontDescription *desc;
169 char *font;
170 int m;
171 GSList *a;
172
173 desc = pango_font_describe (font: item->analysis.font);
174 font = pango_font_description_to_string (desc);
175
176 if (l != items)
177 sep = "|";
178 g_string_append (string: s1, val: sep);
179 append_text (s: s1, text: text + item->offset, len: item->length);
180
181 g_string_append_printf (string: s2, format: "%s%s", sep, font);
182 g_string_append_printf (string: s3, format: "%s%s", sep, get_script_name (s: item->analysis.script));
183 g_string_append_printf (string: s4, format: "%s%s", sep, pango_language_to_string (item->analysis.language));
184 g_string_append_printf (string: s5, format: "%s%d", sep, item->analysis.level);
185 g_string_append_printf (string: s6, format: "%s", sep);
186 g_string_append_printf (string: s7, format: "%s%d(%d)", sep, item->num_chars, get_item_char_offset (item));
187 for (a = item->analysis.extra_attrs; a; a = a->next)
188 {
189 PangoAttribute *attr = a->data;
190 if (a != item->analysis.extra_attrs)
191 g_string_append (string: s6, val: ",");
192 print_attribute (attr, string: s6);
193 }
194
195 g_free (mem: font);
196 pango_font_description_free (desc);
197
198 m = MAX (MAX (MAX (s1->len, s2->len),
199 MAX (s3->len, s4->len)),
200 MAX (s5->len, s6->len));
201
202 g_string_append_printf (string: s1, format: "%*s", (int)(m - s1->len), "");
203 g_string_append_printf (string: s2, format: "%*s", (int)(m - s2->len), "");
204 g_string_append_printf (string: s3, format: "%*s", (int)(m - s3->len), "");
205 g_string_append_printf (string: s4, format: "%*s", (int)(m - s4->len), "");
206 g_string_append_printf (string: s5, format: "%*s", (int)(m - s5->len), "");
207 g_string_append_printf (string: s6, format: "%*s", (int)(m - s6->len), "");
208 g_string_append_printf (string: s7, format: "%*s", (int)(m - s7->len), "");
209 }
210
211 g_string_append_printf (string, format: "%s\n", test);
212 g_string_append_printf (string, format: "%s\n", s1->str);
213 g_string_append_printf (string, format: "%s\n", s7->str);
214 g_string_append_printf (string, format: "%s\n", s2->str);
215 g_string_append_printf (string, format: "%s\n", s3->str);
216 g_string_append_printf (string, format: "%s\n", s4->str);
217 g_string_append_printf (string, format: "%s\n", s5->str);
218 g_string_append_printf (string, format: "%s\n", s6->str);
219
220 g_string_free (string: s1, TRUE);
221 g_string_free (string: s2, TRUE);
222 g_string_free (string: s3, TRUE);
223 g_string_free (string: s4, TRUE);
224 g_string_free (string: s5, TRUE);
225 g_string_free (string: s6, TRUE);
226 g_string_free (string: s7, TRUE);
227
228 g_list_free_full (list: items, free_func: (GDestroyNotify)pango_item_free);
229 pango_attr_list_unref (list: attrs);
230 g_free (mem: text);
231 g_free (mem: contents);
232}
233
234static gchar *
235get_expected_filename (const gchar *filename)
236{
237 gchar *f, *p, *expected;
238
239 f = g_strdup (str: filename);
240 p = strstr (haystack: f, needle: ".items");
241 if (p)
242 *p = 0;
243 expected = g_strconcat (string1: f, ".expected", NULL);
244
245 g_free (mem: f);
246
247 return expected;
248}
249
250static void
251test_itemize (gconstpointer d)
252{
253 const gchar *filename = d;
254 gchar *expected_file;
255 GError *error = NULL;
256 GString *dump;
257 gchar *diff;
258 PangoFontFamily **families;
259 int n_families;
260 gboolean found_cantarell;
261
262 char *old_locale = g_strdup (str: setlocale (LC_ALL, NULL));
263 setlocale (LC_ALL, locale: "en_US.UTF-8");
264 if (strstr (haystack: setlocale (LC_ALL, NULL), needle: "en_US") == NULL)
265 {
266 char *msg = g_strdup_printf (format: "Locale en_US.UTF-8 not available, skipping itemization %s", filename);
267 g_test_skip (msg);
268 g_free (mem: msg);
269 g_free (mem: old_locale);
270 return;
271 }
272
273 found_cantarell = FALSE;
274 pango_context_list_families (context, families: &families, n_families: &n_families);
275 for (int i = 0; i < n_families; i++)
276 {
277 if (strcmp (s1: pango_font_family_get_name (family: families[i]), s2: "Cantarell") == 0)
278 {
279 found_cantarell = TRUE;
280 break;
281 }
282 }
283 g_free (mem: families);
284
285 if (!found_cantarell)
286 {
287 char *msg = g_strdup_printf (format: "Cantarell font not available, skipping itemization %s", filename);
288 g_test_skip (msg);
289 g_free (mem: msg);
290 g_free (mem: old_locale);
291 return;
292 }
293
294 expected_file = get_expected_filename (filename);
295
296 dump = g_string_sized_new (dfl_size: 0);
297
298 test_file (filename, string: dump);
299
300 diff = diff_with_file (file: expected_file, text: dump->str, len: dump->len, error: &error);
301 g_assert_no_error (error);
302
303 setlocale (LC_ALL, locale: old_locale);
304 g_free (mem: old_locale);
305
306 if (diff && diff[0])
307 {
308 char **lines = g_strsplit (string: diff, delimiter: "\n", max_tokens: -1);
309 const char *line;
310 int i = 0;
311
312 g_test_message (format: "Contents don't match expected contents");
313
314 for (line = lines[0]; line != NULL; line = lines[++i])
315 g_test_message (format: "%s", line);
316
317 g_test_fail ();
318
319 g_strfreev (str_array: lines);
320 }
321
322 g_free (mem: diff);
323 g_string_free (string: dump, TRUE);
324 g_free (mem: expected_file);
325}
326
327int
328main (int argc, char *argv[])
329{
330 GDir *dir;
331 GError *error = NULL;
332 const gchar *name;
333 gchar *path;
334
335 context = pango_font_map_create_context (fontmap: pango_cairo_font_map_get_default ());
336 pango_context_set_language (context, language: pango_language_from_string (language: "en-us"));
337
338 /* allow to easily generate expected output for new test cases */
339 if (argc > 1 && argv[1][0] != '-')
340 {
341 GString *string;
342
343 string = g_string_sized_new (dfl_size: 0);
344 test_file (filename: argv[1], string);
345 printf (format: "%s", string->str);
346
347 return 0;
348 }
349
350 g_test_init (argc: &argc, argv: &argv, NULL);
351
352 path = g_test_build_filename (file_type: G_TEST_DIST, first_path: "itemize", NULL);
353 dir = g_dir_open (path, flags: 0, error: &error);
354 g_free (mem: path);
355 g_assert_no_error (error);
356 while ((name = g_dir_read_name (dir)) != NULL)
357 {
358 if (!strstr (haystack: name, needle: "items"))
359 continue;
360
361 path = g_strdup_printf (format: "/itemize/%s", name);
362 g_test_add_data_func_full (testpath: path, test_data: g_test_build_filename (file_type: G_TEST_DIST, first_path: "itemize", name, NULL),
363 test_func: test_itemize, data_free_func: g_free);
364 g_free (mem: path);
365 }
366 g_dir_close (dir);
367
368 return g_test_run ();
369}
370

source code of gtk/subprojects/pango/tests/test-itemize.c