1 | /* Pango |
2 | * testmisc.c: Test program for miscellaneous things |
3 | * |
4 | * Copyright (C) 2020 Matthias Clasen |
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 "config.h" |
23 | #include <glib.h> |
24 | #include <pango/pangocairo.h> |
25 | |
26 | #ifdef HAVE_CAIRO_FREETYPE |
27 | #include <pango/pango-ot.h> |
28 | #endif |
29 | |
30 | /* test that we don't crash in shape_tab when the layout |
31 | * is such that we don't have effective attributes |
32 | */ |
33 | static void |
34 | test_shape_tab_crash (void) |
35 | { |
36 | PangoContext *context; |
37 | PangoLayout *layout; |
38 | |
39 | context = pango_font_map_create_context (fontmap: pango_cairo_font_map_get_default ()); |
40 | layout = pango_layout_new (context); |
41 | pango_layout_set_text (layout, text: "one\ttwo" , length: -1); |
42 | pango_layout_is_ellipsized (layout); |
43 | |
44 | g_object_unref (object: layout); |
45 | g_object_unref (object: context); |
46 | } |
47 | |
48 | /* Test that itemizing a string with 0 characters works |
49 | */ |
50 | static void |
51 | test_itemize_empty_crash (void) |
52 | { |
53 | PangoContext *context; |
54 | |
55 | context = pango_font_map_create_context (fontmap: pango_cairo_font_map_get_default ()); |
56 | pango_itemize_with_base_dir (context, base_dir: PANGO_DIRECTION_LTR, text: "" , start_index: 0, length: 1, NULL, NULL); |
57 | |
58 | g_object_unref (object: context); |
59 | } |
60 | |
61 | static void |
62 | test_itemize_utf8 (void) |
63 | { |
64 | PangoContext *context; |
65 | GList *result = NULL; |
66 | |
67 | context = pango_font_map_create_context (fontmap: pango_cairo_font_map_get_default ()); |
68 | result = pango_itemize_with_base_dir (context, base_dir: PANGO_DIRECTION_LTR, text: "\xc3\xa1\na" , start_index: 3, length: 1, NULL, NULL); |
69 | g_assert (result != NULL); |
70 | |
71 | g_list_free_full (list: result, free_func: (GDestroyNotify)pango_item_free); |
72 | g_object_unref (object: context); |
73 | } |
74 | |
75 | /* Test that pango_layout_set_text (layout, "short", 200) |
76 | * does not lead to a crash. (pidgin does this) |
77 | */ |
78 | static void |
79 | test_short_string_crash (void) |
80 | { |
81 | PangoContext *context; |
82 | PangoLayout *layout; |
83 | int width, height; |
84 | |
85 | context = pango_font_map_create_context (fontmap: pango_cairo_font_map_get_default ()); |
86 | layout = pango_layout_new (context); |
87 | pango_layout_set_text (layout, text: "short text" , length: 200); |
88 | pango_layout_get_pixel_size (layout, width: &width, height: &height); |
89 | |
90 | g_object_unref (object: layout); |
91 | g_object_unref (object: context); |
92 | } |
93 | |
94 | static void |
95 | test_language_emoji_crash (void) |
96 | { |
97 | PangoLanguage *lang; |
98 | const PangoScript *scripts; |
99 | int num; |
100 | |
101 | lang = pango_language_from_string (language: "und-zsye" ); |
102 | scripts = pango_language_get_scripts (language: lang, num_scripts: &num); |
103 | |
104 | g_assert (num >= 0); |
105 | g_assert (scripts == NULL || num > 0); |
106 | } |
107 | |
108 | static void |
109 | test_line_height (void) |
110 | { |
111 | PangoContext *context; |
112 | PangoLayout *layout; |
113 | PangoLayoutLine *line; |
114 | int height = 0; |
115 | |
116 | context = pango_font_map_create_context (fontmap: pango_cairo_font_map_get_default ()); |
117 | layout = pango_layout_new (context); |
118 | pango_layout_set_text (layout, text: "one\ttwo" , length: -1); |
119 | line = pango_layout_get_line_readonly (layout, line: 0); |
120 | pango_layout_line_get_height (line, height: &height); |
121 | |
122 | g_assert_cmpint (height, >, 0); |
123 | |
124 | g_object_unref (object: layout); |
125 | g_object_unref (object: context); |
126 | } |
127 | |
128 | static void |
129 | test_line_height2 (void) |
130 | { |
131 | PangoContext *context; |
132 | PangoLayout *layout; |
133 | PangoLayoutLine *line; |
134 | int height1 = 0; |
135 | int height2 = 0; |
136 | |
137 | context = pango_font_map_create_context (fontmap: pango_cairo_font_map_get_default ()); |
138 | layout = pango_layout_new (context); |
139 | pango_layout_set_text (layout, text: "one" , length: -1); |
140 | |
141 | line = pango_layout_get_line_readonly (layout, line: 0); |
142 | pango_layout_line_get_height (line, height: &height1); |
143 | |
144 | pango_layout_set_text (layout, text: "" , length: -1); |
145 | |
146 | line = pango_layout_get_line_readonly (layout, line: 0); |
147 | pango_layout_line_get_height (line, height: &height2); |
148 | |
149 | g_assert_cmpint (height1, ==, height2); |
150 | |
151 | g_object_unref (object: layout); |
152 | g_object_unref (object: context); |
153 | } |
154 | |
155 | static void |
156 | test_line_height3 (void) |
157 | { |
158 | PangoContext *context; |
159 | PangoLayout *layout; |
160 | PangoLayoutLine *line; |
161 | PangoAttrList *attrs; |
162 | int height1 = 0; |
163 | int height2 = 0; |
164 | |
165 | context = pango_font_map_create_context (fontmap: pango_cairo_font_map_get_default ()); |
166 | layout = pango_layout_new (context); |
167 | pango_layout_set_text (layout, text: "one" , length: -1); |
168 | attrs = pango_attr_list_new (); |
169 | pango_attr_list_insert (list: attrs, attr: pango_attr_line_height_new (factor: 2.0)); |
170 | pango_layout_set_attributes (layout, attrs); |
171 | pango_attr_list_unref (list: attrs); |
172 | |
173 | line = pango_layout_get_line_readonly (layout, line: 0); |
174 | pango_layout_line_get_height (line, height: &height1); |
175 | |
176 | pango_layout_set_text (layout, text: "" , length: -1); |
177 | |
178 | line = pango_layout_get_line_readonly (layout, line: 0); |
179 | pango_layout_line_get_height (line, height: &height2); |
180 | |
181 | g_assert_cmpint (height1, ==, height2); |
182 | |
183 | g_object_unref (object: layout); |
184 | g_object_unref (object: context); |
185 | } |
186 | |
187 | static void |
188 | test_run_height (void) |
189 | { |
190 | PangoContext *context; |
191 | PangoLayout *layout; |
192 | PangoLayoutIter *iter; |
193 | PangoRectangle logical1, logical2; |
194 | |
195 | if (strcmp (G_OBJECT_TYPE_NAME (pango_cairo_font_map_get_default ()), s2: "PangoCairoCoreTextFontMap" ) == 0) |
196 | { |
197 | g_test_skip (msg: "This test fails on macOS and needs debugging" ); |
198 | return; |
199 | } |
200 | |
201 | context = pango_font_map_create_context (fontmap: pango_cairo_font_map_get_default ()); |
202 | layout = pango_layout_new (context); |
203 | pango_layout_set_text (layout, text: "one" , length: -1); |
204 | |
205 | iter = pango_layout_get_iter (layout); |
206 | pango_layout_iter_get_run_extents (iter, NULL, logical_rect: &logical1); |
207 | pango_layout_iter_free (iter); |
208 | |
209 | pango_layout_set_text (layout, text: "" , length: -1); |
210 | |
211 | iter = pango_layout_get_iter (layout); |
212 | pango_layout_iter_get_run_extents (iter, NULL, logical_rect: &logical2); |
213 | pango_layout_iter_free (iter); |
214 | |
215 | g_assert_cmpint (logical1.height, ==, logical2.height); |
216 | |
217 | g_object_unref (object: layout); |
218 | g_object_unref (object: context); |
219 | } |
220 | |
221 | static void |
222 | test_cursor_height (void) |
223 | { |
224 | PangoContext *context; |
225 | PangoLayout *layout; |
226 | PangoRectangle strong; |
227 | |
228 | context = pango_font_map_create_context (fontmap: pango_cairo_font_map_get_default ()); |
229 | layout = pango_layout_new (context); |
230 | pango_layout_set_text (layout, text: "one\ttwo" , length: -1); |
231 | pango_layout_get_cursor_pos (layout, index_: 0, strong_pos: &strong, NULL); |
232 | |
233 | g_assert_cmpint (strong.height, >, 0); |
234 | |
235 | g_object_unref (object: layout); |
236 | g_object_unref (object: context); |
237 | } |
238 | |
239 | static void |
240 | test_cursor_height2 (void) |
241 | { |
242 | PangoContext *context; |
243 | PangoLayout *layout; |
244 | PangoRectangle strong1, strong2; |
245 | |
246 | if (strcmp (G_OBJECT_TYPE_NAME (pango_cairo_font_map_get_default ()), s2: "PangoCairoCoreTextFontMap" ) == 0) |
247 | { |
248 | g_test_skip (msg: "This test fails on macOS and needs debugging" ); |
249 | return; |
250 | } |
251 | |
252 | context = pango_font_map_create_context (fontmap: pango_cairo_font_map_get_default ()); |
253 | layout = pango_layout_new (context); |
254 | pango_layout_set_text (layout, text: "one" , length: -1); |
255 | |
256 | pango_layout_get_cursor_pos (layout, index_: 0, strong_pos: &strong1, NULL); |
257 | |
258 | pango_layout_set_text (layout, text: "" , length: -1); |
259 | |
260 | pango_layout_get_cursor_pos (layout, index_: 0, strong_pos: &strong2, NULL); |
261 | |
262 | g_assert_cmpint (strong1.height, ==, strong2.height); |
263 | |
264 | g_object_unref (object: layout); |
265 | g_object_unref (object: context); |
266 | } |
267 | |
268 | static void |
269 | test_attr_list_update (void) |
270 | { |
271 | PangoAttribute *weight_attr; |
272 | PangoAttribute *fg_attr; |
273 | PangoAttrList *list; |
274 | |
275 | weight_attr = pango_attr_weight_new (weight: 700); |
276 | weight_attr->start_index = 4; |
277 | weight_attr->end_index = 6; |
278 | |
279 | fg_attr = pango_attr_foreground_new (red: 0, green: 0, blue: 65535); |
280 | fg_attr->start_index = PANGO_ATTR_INDEX_FROM_TEXT_BEGINNING; |
281 | fg_attr->end_index = PANGO_ATTR_INDEX_TO_TEXT_END; |
282 | |
283 | list = pango_attr_list_new(); |
284 | pango_attr_list_insert (list, attr: weight_attr); |
285 | pango_attr_list_insert (list, attr: fg_attr); |
286 | |
287 | g_assert_cmpuint (weight_attr->start_index, ==, 4); |
288 | g_assert_cmpuint (weight_attr->end_index, ==, 6); |
289 | g_assert_cmpuint (fg_attr->start_index, ==, PANGO_ATTR_INDEX_FROM_TEXT_BEGINNING); |
290 | g_assert_cmpuint (fg_attr->end_index, ==, PANGO_ATTR_INDEX_TO_TEXT_END); |
291 | |
292 | // Delete 1 byte at position 2 |
293 | pango_attr_list_update (list, pos: 2, remove: 1, add: 0); |
294 | |
295 | g_assert_cmpuint (weight_attr->start_index, ==, 3); |
296 | g_assert_cmpuint (weight_attr->end_index, ==, 5); |
297 | g_assert_cmpuint (fg_attr->start_index, ==, PANGO_ATTR_INDEX_FROM_TEXT_BEGINNING); |
298 | g_assert_cmpuint (fg_attr->end_index, ==, PANGO_ATTR_INDEX_TO_TEXT_END); |
299 | |
300 | pango_attr_list_unref (list); |
301 | } |
302 | |
303 | static void |
304 | test_version_info (void) |
305 | { |
306 | char *str; |
307 | |
308 | g_assert_null (pango_version_check (1, 0, 0)); |
309 | g_assert_null (pango_version_check (PANGO_VERSION_MAJOR, PANGO_VERSION_MINOR, PANGO_VERSION_MICRO)); |
310 | g_assert_nonnull (pango_version_check (2, 0, 0)); |
311 | |
312 | str = g_strdup_printf (format: "%d.%d.%d" , PANGO_VERSION_MAJOR, PANGO_VERSION_MINOR, PANGO_VERSION_MICRO); |
313 | g_assert_cmpstr (str, ==, pango_version_string ()); |
314 | g_free (mem: str); |
315 | } |
316 | |
317 | static void |
318 | test_is_zero_width (void) |
319 | { |
320 | g_assert_true (pango_is_zero_width (0x00ad)); |
321 | g_assert_true (pango_is_zero_width (0x034f)); |
322 | g_assert_false (pango_is_zero_width ('a')); |
323 | g_assert_false (pango_is_zero_width ('c')); |
324 | |
325 | g_assert_true (pango_is_zero_width (0x2066)); |
326 | g_assert_true (pango_is_zero_width (0x2067)); |
327 | g_assert_true (pango_is_zero_width (0x2068)); |
328 | g_assert_true (pango_is_zero_width (0x2069)); |
329 | |
330 | g_assert_true (pango_is_zero_width (0x202a)); |
331 | g_assert_true (pango_is_zero_width (0x202b)); |
332 | g_assert_true (pango_is_zero_width (0x202c)); |
333 | g_assert_true (pango_is_zero_width (0x202d)); |
334 | g_assert_true (pango_is_zero_width (0x202e)); |
335 | } |
336 | |
337 | static void |
338 | test_gravity_to_rotation (void) |
339 | { |
340 | g_assert_true (pango_gravity_to_rotation (PANGO_GRAVITY_SOUTH) == 0); |
341 | g_assert_true (pango_gravity_to_rotation (PANGO_GRAVITY_NORTH) == G_PI); |
342 | g_assert_true (pango_gravity_to_rotation (PANGO_GRAVITY_EAST) == -G_PI_2); |
343 | g_assert_true (pango_gravity_to_rotation (PANGO_GRAVITY_WEST) == G_PI_2); |
344 | } |
345 | |
346 | static void |
347 | test_gravity_from_matrix (void) |
348 | { |
349 | PangoMatrix m = PANGO_MATRIX_INIT; |
350 | |
351 | g_assert_true (pango_gravity_get_for_matrix (&m) == PANGO_GRAVITY_SOUTH); |
352 | |
353 | pango_matrix_rotate (matrix: &m, degrees: 90); |
354 | g_assert_true (pango_gravity_get_for_matrix (&m) == PANGO_GRAVITY_WEST); |
355 | |
356 | pango_matrix_rotate (matrix: &m, degrees: 90); |
357 | g_assert_true (pango_gravity_get_for_matrix (&m) == PANGO_GRAVITY_NORTH); |
358 | |
359 | pango_matrix_rotate (matrix: &m, degrees: 90); |
360 | g_assert_true (pango_gravity_get_for_matrix (&m) == PANGO_GRAVITY_EAST); |
361 | } |
362 | |
363 | static void |
364 | test_gravity_for_script (void) |
365 | { |
366 | struct { |
367 | PangoScript script; |
368 | PangoGravity gravity; |
369 | PangoGravity gravity_natural; |
370 | PangoGravity gravity_line; |
371 | } tests[] = { |
372 | { PANGO_SCRIPT_ARABIC, PANGO_GRAVITY_SOUTH, PANGO_GRAVITY_SOUTH, PANGO_GRAVITY_NORTH }, |
373 | { PANGO_SCRIPT_BOPOMOFO, PANGO_GRAVITY_EAST, PANGO_GRAVITY_SOUTH, PANGO_GRAVITY_SOUTH }, |
374 | { PANGO_SCRIPT_LATIN, PANGO_GRAVITY_SOUTH, PANGO_GRAVITY_SOUTH, PANGO_GRAVITY_SOUTH }, |
375 | { PANGO_SCRIPT_HANGUL, PANGO_GRAVITY_EAST, PANGO_GRAVITY_SOUTH, PANGO_GRAVITY_SOUTH }, |
376 | { PANGO_SCRIPT_MONGOLIAN, PANGO_GRAVITY_WEST, PANGO_GRAVITY_SOUTH }, |
377 | { PANGO_SCRIPT_OGHAM, PANGO_GRAVITY_WEST, PANGO_GRAVITY_NORTH, PANGO_GRAVITY_SOUTH }, |
378 | { PANGO_SCRIPT_TIBETAN, PANGO_GRAVITY_SOUTH, PANGO_GRAVITY_SOUTH, PANGO_GRAVITY_SOUTH }, |
379 | }; |
380 | |
381 | for (int i = 0; i < G_N_ELEMENTS (tests); i++) |
382 | { |
383 | g_assert_true (pango_gravity_get_for_script (tests[i].script, PANGO_GRAVITY_AUTO, PANGO_GRAVITY_HINT_STRONG) == tests[i].gravity); |
384 | |
385 | g_assert_true (pango_gravity_get_for_script_and_width (tests[i].script, FALSE, PANGO_GRAVITY_EAST, PANGO_GRAVITY_HINT_NATURAL) == tests[i].gravity_natural); |
386 | g_assert_true (pango_gravity_get_for_script_and_width (tests[i].script, FALSE, PANGO_GRAVITY_EAST, PANGO_GRAVITY_HINT_STRONG) == PANGO_GRAVITY_EAST); |
387 | g_assert_true (pango_gravity_get_for_script_and_width (tests[i].script, FALSE, PANGO_GRAVITY_EAST, PANGO_GRAVITY_HINT_LINE) == tests[i].gravity_line); |
388 | } |
389 | } |
390 | |
391 | #ifdef HAVE_CAIRO_FREETYPE |
392 | G_GNUC_BEGIN_IGNORE_DEPRECATIONS |
393 | |
394 | static void |
395 | test_language_to_tag (void) |
396 | { |
397 | PangoLanguage *lang; |
398 | PangoOTTag tag; |
399 | PangoLanguage *lang2; |
400 | |
401 | lang = pango_language_from_string (language: "de" ); |
402 | |
403 | tag = pango_ot_tag_from_language (language: lang); |
404 | |
405 | lang2 = pango_ot_tag_to_language (language_tag: tag); |
406 | |
407 | g_assert_true (lang2 == lang); |
408 | } |
409 | |
410 | G_GNUC_END_IGNORE_DEPRECATIONS |
411 | #endif |
412 | |
413 | static void |
414 | test_fallback_shape (void) |
415 | { |
416 | PangoContext *context; |
417 | const char *text; |
418 | GList *items, *l; |
419 | |
420 | context = pango_font_map_create_context (fontmap: pango_cairo_font_map_get_default ()); |
421 | |
422 | text = "Some text to shape ﺄﻧﺍ ﻕﺍﺩﺭ ﻊﻟﻯ ﺄﻜﻟ ﺎﻟﺰﺟﺎﺟ ﻭ ﻩﺫﺍ ﻻ ﻱﺆﻠﻤﻨﻳ" ; |
423 | items = pango_itemize (context, text, start_index: 0, length: strlen (s: text), NULL, NULL); |
424 | for (l = items; l; l = l->next) |
425 | { |
426 | PangoItem *item = l->data; |
427 | PangoGlyphString *glyphs; |
428 | |
429 | /* We want to test fallback shaping, which happens when we don't have a font */ |
430 | g_clear_object (&item->analysis.font); |
431 | |
432 | glyphs = pango_glyph_string_new (); |
433 | pango_shape_full (item_text: text + item->offset, item_length: item->length, NULL, paragraph_length: 0, analysis: &item->analysis, glyphs); |
434 | |
435 | for (int i = 0; i < glyphs->num_glyphs; i++) |
436 | { |
437 | PangoGlyph glyph = glyphs->glyphs[i].glyph; |
438 | g_assert_true (glyph == PANGO_GLYPH_EMPTY || (glyph & PANGO_GLYPH_UNKNOWN_FLAG)); |
439 | } |
440 | |
441 | pango_glyph_string_free (string: glyphs); |
442 | } |
443 | |
444 | g_list_free_full (list: items, free_func: (GDestroyNotify)pango_item_free); |
445 | |
446 | g_object_unref (object: context); |
447 | } |
448 | |
449 | /* https://bugzilla.gnome.org/show_bug.cgi?id=547303 */ |
450 | static void |
451 | test_get_cursor_crash (void) |
452 | { |
453 | PangoContext *context; |
454 | PangoLayout *layout; |
455 | int i; |
456 | |
457 | const char *string = "foo\n\rbar\r\nbaz\n\nqux\n\n.." ; |
458 | |
459 | context = pango_font_map_create_context (fontmap: pango_cairo_font_map_get_default ()); |
460 | |
461 | layout = pango_layout_new (context); |
462 | |
463 | pango_layout_set_text (layout, text: string, length: -1); |
464 | |
465 | for (i = 0; string[i]; i++) |
466 | { |
467 | PangoRectangle rectA, rectB; |
468 | |
469 | pango_layout_get_cursor_pos (layout, index_: i, strong_pos: &rectA, weak_pos: &rectB); |
470 | g_assert_cmpint (rectA.x, ==, rectB.x); |
471 | } |
472 | |
473 | g_object_unref (object: layout); |
474 | g_object_unref (object: context); |
475 | } |
476 | |
477 | /* Test that get_cursor returns split cursors in the |
478 | * expected situations. In particular, this was broken |
479 | * at the end of the string here. |
480 | */ |
481 | static void |
482 | test_get_cursor (void) |
483 | { |
484 | const char *text = "abאב" ; |
485 | PangoContext *context; |
486 | PangoLayout *layout; |
487 | PangoRectangle strong, weak; |
488 | |
489 | context = pango_font_map_create_context (fontmap: pango_cairo_font_map_get_default ()); |
490 | |
491 | layout = pango_layout_new (context); |
492 | pango_layout_set_text (layout, text, length: -1); |
493 | |
494 | pango_layout_get_cursor_pos (layout, index_: 0, strong_pos: &strong, weak_pos: &weak); |
495 | g_assert_cmpint (strong.x, ==, weak.x); |
496 | |
497 | pango_layout_get_cursor_pos (layout, index_: 1, strong_pos: &strong, weak_pos: &weak); |
498 | g_assert_cmpint (strong.x, ==, weak.x); |
499 | |
500 | pango_layout_get_cursor_pos (layout, index_: 2, strong_pos: &strong, weak_pos: &weak); |
501 | g_assert_cmpint (strong.x, !=, weak.x); |
502 | |
503 | pango_layout_get_cursor_pos (layout, index_: 4, strong_pos: &strong, weak_pos: &weak); |
504 | g_assert_cmpint (strong.x, ==, weak.x); |
505 | |
506 | pango_layout_get_cursor_pos (layout, index_: 6, strong_pos: &strong, weak_pos: &weak); |
507 | g_assert_cmpint (strong.x, !=, weak.x); |
508 | |
509 | g_object_unref (object: layout); |
510 | g_object_unref (object: context); |
511 | } |
512 | |
513 | static void |
514 | test_index_to_x (void) |
515 | { |
516 | PangoContext *context; |
517 | const char *tests[] = { |
518 | "acually" , // soft hyphen |
519 | "acually" , // zero-width space |
520 | }; |
521 | |
522 | context = pango_font_map_create_context (fontmap: pango_cairo_font_map_get_default ()); |
523 | |
524 | for (int i = 0; i < G_N_ELEMENTS (tests); i++) |
525 | { |
526 | PangoLayout *layout; |
527 | const char *text; |
528 | const char *p; |
529 | |
530 | layout = pango_layout_new (context); |
531 | pango_layout_set_text (layout, text: tests[i], length: -1); |
532 | text = pango_layout_get_text (layout); |
533 | |
534 | for (p = text; *p; p = g_utf8_next_char (p)) |
535 | { |
536 | int index = p - text; |
537 | int l; |
538 | PangoLayoutLine *line; |
539 | int x; |
540 | int index2, trailing; |
541 | gunichar ch; |
542 | |
543 | ch = g_utf8_get_char (p); |
544 | |
545 | pango_layout_index_to_line_x (layout, index_: index, FALSE, line: &l, NULL); |
546 | line = pango_layout_get_line (layout, line: l); |
547 | g_assert_nonnull (line); |
548 | |
549 | pango_layout_line_index_to_x (line, index_: index, trailing: 0, x_pos: &x); |
550 | pango_layout_line_x_to_index (line, x_pos: x, index_: &index2, trailing: &trailing); |
551 | if (!pango_is_zero_width (ch)) |
552 | g_assert_cmpint (index, ==, index2); |
553 | } |
554 | |
555 | g_object_unref (object: layout); |
556 | } |
557 | |
558 | g_object_unref (object: context); |
559 | } |
560 | |
561 | static gboolean |
562 | pango_rectangle_contains (const PangoRectangle *r1, |
563 | const PangoRectangle *r2) |
564 | { |
565 | return r2->x >= r1->x && |
566 | r2->y >= r1->y && |
567 | r2->x + r2->width <= r1->x + r1->width && |
568 | r2->y + r2->height <= r1->y + r1->height; |
569 | } |
570 | |
571 | static void |
572 | test_extents (void) |
573 | { |
574 | PangoContext *context; |
575 | const char *tests[] = { |
576 | "Some long text that has multiple lines that are wrapped by Pango." |
577 | }; |
578 | |
579 | context = pango_font_map_create_context (fontmap: pango_cairo_font_map_get_default ()); |
580 | |
581 | for (int i = 0; i < G_N_ELEMENTS (tests); i++) |
582 | { |
583 | PangoLayout *layout; |
584 | PangoLayoutIter *iter; |
585 | PangoRectangle layout_extents; |
586 | PangoRectangle line_extents; |
587 | PangoRectangle run_extents; |
588 | PangoRectangle cluster_extents; |
589 | PangoRectangle char_extents; |
590 | PangoRectangle pos; |
591 | |
592 | layout = pango_layout_new (context); |
593 | pango_layout_set_text (layout, text: tests[i], length: -1); |
594 | pango_layout_set_width (layout, width: 60 * PANGO_SCALE); |
595 | |
596 | pango_layout_get_extents (layout, NULL, logical_rect: &layout_extents); |
597 | |
598 | iter = pango_layout_get_iter (layout); |
599 | |
600 | do |
601 | { |
602 | pango_layout_iter_get_line_extents (iter, NULL, logical_rect: &line_extents); |
603 | pango_layout_iter_get_run_extents (iter, NULL, logical_rect: &run_extents); |
604 | pango_layout_iter_get_cluster_extents (iter, NULL, logical_rect: &cluster_extents); |
605 | pango_layout_iter_get_char_extents (iter, logical_rect: &char_extents); |
606 | |
607 | pango_layout_index_to_pos (layout, |
608 | index_: pango_layout_iter_get_index (iter), |
609 | pos: &pos); |
610 | if (pos.width < 0) |
611 | { |
612 | pos.x += pos.width; |
613 | pos.width = - pos.width; |
614 | } |
615 | |
616 | g_assert_true (pango_rectangle_contains (&layout_extents, &line_extents)); |
617 | g_assert_true (pango_rectangle_contains (&line_extents, &run_extents)); |
618 | g_assert_true (pango_rectangle_contains (&run_extents, &cluster_extents)); |
619 | g_assert_true (pango_rectangle_contains (&cluster_extents, &char_extents)); |
620 | |
621 | g_assert_true (pango_rectangle_contains (&line_extents, &pos)); |
622 | } |
623 | while (pango_layout_iter_next_char (iter)); |
624 | |
625 | pango_layout_iter_free (iter); |
626 | g_object_unref (object: layout); |
627 | } |
628 | |
629 | g_object_unref (object: context); |
630 | } |
631 | |
632 | static void |
633 | test_empty_line_height (void) |
634 | { |
635 | PangoContext *context; |
636 | PangoLayout *layout; |
637 | PangoFontDescription *description; |
638 | PangoRectangle ext1, ext2, ext3; |
639 | cairo_font_options_t *options; |
640 | int hint; |
641 | int size; |
642 | |
643 | if (strcmp (G_OBJECT_TYPE_NAME (pango_cairo_font_map_get_default ()), s2: "PangoCairoCoreTextFontMap" ) == 0) |
644 | { |
645 | g_test_skip (msg: "This test fails on macOS and needs debugging" ); |
646 | return; |
647 | } |
648 | |
649 | context = pango_font_map_create_context (fontmap: pango_cairo_font_map_get_default ()); |
650 | description = pango_font_description_new (); |
651 | |
652 | for (size = 10; size <= 20; size++) |
653 | { |
654 | pango_font_description_set_size (desc: description, size); |
655 | |
656 | for (hint = CAIRO_HINT_METRICS_OFF; hint <= CAIRO_HINT_METRICS_ON; hint++) |
657 | { |
658 | options = cairo_font_options_create (); |
659 | cairo_font_options_set_hint_metrics (options, hint_metrics: hint); |
660 | pango_cairo_context_set_font_options (context, options); |
661 | cairo_font_options_destroy (options); |
662 | |
663 | layout = pango_layout_new (context); |
664 | pango_layout_set_font_description (layout, desc: description); |
665 | |
666 | pango_layout_get_extents (layout, NULL, logical_rect: &ext1); |
667 | |
668 | pango_layout_set_text (layout, text: "a" , length: 1); |
669 | |
670 | pango_layout_get_extents (layout, NULL, logical_rect: &ext2); |
671 | |
672 | g_assert_cmpint (ext1.height, ==, ext2.height); |
673 | |
674 | pango_layout_set_text (layout, text: "Pg" , length: 1); |
675 | |
676 | pango_layout_get_extents (layout, NULL, logical_rect: &ext3); |
677 | |
678 | g_assert_cmpint (ext2.height, ==, ext3.height); |
679 | |
680 | g_object_unref (object: layout); |
681 | } |
682 | } |
683 | |
684 | pango_font_description_free (desc: description); |
685 | g_object_unref (object: context); |
686 | } |
687 | |
688 | static void |
689 | test_gravity_metrics (void) |
690 | { |
691 | PangoFontMap *map; |
692 | PangoContext *context; |
693 | PangoFontDescription *desc; |
694 | PangoFont *font; |
695 | PangoGlyph glyph; |
696 | PangoGravity gravity; |
697 | PangoRectangle ink[4]; |
698 | PangoRectangle log[4]; |
699 | |
700 | map = pango_cairo_font_map_get_default (); |
701 | context = pango_font_map_create_context (fontmap: map); |
702 | |
703 | desc = pango_font_description_from_string (str: "Cantarell 64" ); |
704 | |
705 | glyph = 1; /* A */ |
706 | |
707 | for (gravity = PANGO_GRAVITY_SOUTH; gravity <= PANGO_GRAVITY_WEST; gravity++) |
708 | { |
709 | pango_font_description_set_gravity (desc, gravity); |
710 | font = pango_font_map_load_font (fontmap: map, context, desc); |
711 | pango_font_get_glyph_extents (font, glyph, ink_rect: &ink[gravity], logical_rect: &log[gravity]); |
712 | g_object_unref (object: font); |
713 | } |
714 | |
715 | g_assert_cmpint (ink[PANGO_GRAVITY_EAST].width, ==, ink[PANGO_GRAVITY_SOUTH].height); |
716 | g_assert_cmpint (ink[PANGO_GRAVITY_EAST].height, ==, ink[PANGO_GRAVITY_SOUTH].width); |
717 | g_assert_cmpint (ink[PANGO_GRAVITY_NORTH].width, ==, ink[PANGO_GRAVITY_SOUTH].width); |
718 | g_assert_cmpint (ink[PANGO_GRAVITY_NORTH].height, ==, ink[PANGO_GRAVITY_SOUTH].height); |
719 | g_assert_cmpint (ink[PANGO_GRAVITY_WEST].width, ==, ink[PANGO_GRAVITY_SOUTH].height); |
720 | g_assert_cmpint (ink[PANGO_GRAVITY_WEST].height, ==, ink[PANGO_GRAVITY_SOUTH].width); |
721 | |
722 | g_assert_cmpint (log[PANGO_GRAVITY_SOUTH].width, ==, - log[PANGO_GRAVITY_NORTH].width); |
723 | g_assert_cmpint (log[PANGO_GRAVITY_EAST].width, ==, - log[PANGO_GRAVITY_WEST].width); |
724 | |
725 | pango_font_description_free (desc); |
726 | g_object_unref (object: context); |
727 | } |
728 | |
729 | static void |
730 | test_transform_rectangle (void) |
731 | { |
732 | PangoMatrix matrix = PANGO_MATRIX_INIT; |
733 | PangoRectangle rect; |
734 | PangoRectangle rect2; |
735 | |
736 | rect = rect2 = (PangoRectangle) { 10 * PANGO_SCALE, 20 * PANGO_SCALE, 30 * PANGO_SCALE, 40 * PANGO_SCALE }; |
737 | pango_matrix_transform_rectangle (matrix: &matrix, rect: &rect2); |
738 | |
739 | g_assert_cmpint (rect2.x, ==, rect.x); |
740 | g_assert_cmpint (rect2.y, ==, rect.y); |
741 | g_assert_cmpint (rect2.width, ==, rect.width); |
742 | g_assert_cmpint (rect2.height, ==, rect.height); |
743 | |
744 | matrix = (PangoMatrix) PANGO_MATRIX_INIT; |
745 | pango_matrix_translate (matrix: &matrix, tx: 10, ty: 20); |
746 | |
747 | pango_matrix_transform_rectangle (matrix: &matrix, rect: &rect2); |
748 | |
749 | g_assert_cmpint (rect2.x, ==, rect.x + 10 * PANGO_SCALE); |
750 | g_assert_cmpint (rect2.y, ==, rect.y + 20 * PANGO_SCALE); |
751 | g_assert_cmpint (rect2.width, ==, rect.width); |
752 | g_assert_cmpint (rect2.height, ==, rect.height); |
753 | |
754 | rect2 = rect; |
755 | |
756 | matrix = (PangoMatrix) PANGO_MATRIX_INIT; |
757 | pango_matrix_rotate (matrix: &matrix, degrees: -90); |
758 | |
759 | pango_matrix_transform_rectangle (matrix: &matrix, rect: &rect2); |
760 | |
761 | g_assert_cmpint (rect2.x, ==, - (rect.y + rect.height)); |
762 | g_assert_cmpint (rect2.y, ==, rect.x); |
763 | g_assert_cmpint (rect2.width, ==, rect.height); |
764 | g_assert_cmpint (rect2.height, ==, rect.width); |
765 | } |
766 | |
767 | static void |
768 | test_wrap_char (void) |
769 | { |
770 | PangoContext *context; |
771 | PangoLayout *layout; |
772 | int w, h, w0, h0; |
773 | |
774 | context = pango_font_map_create_context (fontmap: pango_cairo_font_map_get_default ()); |
775 | layout = pango_layout_new (context); |
776 | pango_layout_set_text (layout, text: "Rows can have suffix widgets" , length: -1); |
777 | pango_layout_set_wrap (layout, wrap: PANGO_WRAP_WORD_CHAR); |
778 | |
779 | pango_layout_set_width (layout, width: 0); |
780 | pango_layout_get_size (layout, width: &w0, height: &h0); |
781 | |
782 | pango_layout_set_width (layout, width: w0); |
783 | pango_layout_get_size (layout, width: &w, height: &h); |
784 | |
785 | g_assert_cmpint (w0, ==, w); |
786 | g_assert_cmpint (h0, >=, h); |
787 | |
788 | g_object_unref (object: layout); |
789 | g_object_unref (object: context); |
790 | } |
791 | |
792 | /* Test the crash with Small Caps in itemization from #627 */ |
793 | static void |
794 | test_small_caps_crash (void) |
795 | { |
796 | PangoContext *context; |
797 | PangoLayout *layout; |
798 | PangoFontDescription *desc; |
799 | int w, h; |
800 | |
801 | if (strcmp (G_OBJECT_TYPE_NAME (pango_cairo_font_map_get_default ()), s2: "PangoCairoCoreTextFontMap" ) == 0) |
802 | { |
803 | g_test_skip (msg: "This test needs a fontmap that supports Small-Caps" ); |
804 | return; |
805 | } |
806 | |
807 | context = pango_font_map_create_context (fontmap: pango_cairo_font_map_get_default ()); |
808 | layout = pango_layout_new (context); |
809 | desc = pango_font_description_from_string (str: "Cantarell Small-Caps 11" ); |
810 | pango_layout_set_font_description (layout, desc); |
811 | |
812 | pango_layout_set_text (layout, text: "Pere Ràfols Soler\nEqualiser, LV2\nAudio: 1, 1\nMidi: 0, 0\nControls: 53, 2\nCV: 0, 0" , length: -1); |
813 | |
814 | pango_layout_get_size (layout, width: &w, height: &h); |
815 | |
816 | pango_font_description_free (desc); |
817 | g_object_unref (object: layout); |
818 | g_object_unref (object: context); |
819 | } |
820 | |
821 | int |
822 | main (int argc, char *argv[]) |
823 | { |
824 | g_test_init (argc: &argc, argv: &argv, NULL); |
825 | |
826 | g_test_add_func (testpath: "/layout/shape-tab-crash" , test_func: test_shape_tab_crash); |
827 | g_test_add_func (testpath: "/layout/itemize-empty-crash" , test_func: test_itemize_empty_crash); |
828 | g_test_add_func (testpath: "/layout/itemize-utf8" , test_func: test_itemize_utf8); |
829 | g_test_add_func (testpath: "/layout/short-string-crash" , test_func: test_short_string_crash); |
830 | g_test_add_func (testpath: "/language/emoji-crash" , test_func: test_language_emoji_crash); |
831 | g_test_add_func (testpath: "/layout/line-height" , test_func: test_line_height); |
832 | g_test_add_func (testpath: "/layout/line-height2" , test_func: test_line_height2); |
833 | g_test_add_func (testpath: "/layout/line-height3" , test_func: test_line_height3); |
834 | g_test_add_func (testpath: "/layout/run-height" , test_func: test_run_height); |
835 | g_test_add_func (testpath: "/layout/cursor-height" , test_func: test_cursor_height); |
836 | g_test_add_func (testpath: "/layout/cursor-height2" , test_func: test_cursor_height2); |
837 | g_test_add_func (testpath: "/attr-list/update" , test_func: test_attr_list_update); |
838 | g_test_add_func (testpath: "/misc/version-info" , test_func: test_version_info); |
839 | g_test_add_func (testpath: "/misc/is-zerowidth" , test_func: test_is_zero_width); |
840 | g_test_add_func (testpath: "/gravity/to-rotation" , test_func: test_gravity_to_rotation); |
841 | g_test_add_func (testpath: "/gravity/from-matrix" , test_func: test_gravity_from_matrix); |
842 | g_test_add_func (testpath: "/gravity/for-script" , test_func: test_gravity_for_script); |
843 | g_test_add_func (testpath: "/layout/fallback-shape" , test_func: test_fallback_shape); |
844 | #ifdef HAVE_CAIRO_FREETYPE |
845 | g_test_add_func (testpath: "/language/to-tag" , test_func: test_language_to_tag); |
846 | #endif |
847 | g_test_add_func (testpath: "/bidi/get-cursor-crash" , test_func: test_get_cursor_crash); |
848 | g_test_add_func (testpath: "/bidi/get-cursor" , test_func: test_get_cursor); |
849 | g_test_add_func (testpath: "/layout/index-to-x" , test_func: test_index_to_x); |
850 | g_test_add_func (testpath: "/layout/extents" , test_func: test_extents); |
851 | g_test_add_func (testpath: "/layout/empty-line-height" , test_func: test_empty_line_height); |
852 | g_test_add_func (testpath: "/layout/gravity-metrics" , test_func: test_gravity_metrics); |
853 | g_test_add_func (testpath: "/layout/wrap-char" , test_func: test_wrap_char); |
854 | g_test_add_func (testpath: "/matrix/transform-rectangle" , test_func: test_transform_rectangle); |
855 | g_test_add_func (testpath: "/itemize/small-caps-crash" , test_func: test_small_caps_crash); |
856 | |
857 | return g_test_run (); |
858 | } |
859 | |