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 */
33static void
34test_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 */
50static void
51test_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
61static void
62test_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 */
78static void
79test_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
94static void
95test_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
108static void
109test_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
128static void
129test_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
155static void
156test_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
187static void
188test_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
221static void
222test_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
239static void
240test_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
268static void
269test_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
303static void
304test_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
317static void
318test_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
337static void
338test_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
346static void
347test_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
363static void
364test_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
392G_GNUC_BEGIN_IGNORE_DEPRECATIONS
393
394static void
395test_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
410G_GNUC_END_IGNORE_DEPRECATIONS
411#endif
412
413static void
414test_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 sha​pe ﺄﻧﺍ ﻕﺍﺩﺭ ﻊﻟﻯ ﺄﻜﻟ ﺎﻟﺰﺟﺎﺟ ﻭ ﻩﺫﺍ ﻻ ﻱﺆﻠﻤﻨﻳ";
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 */
450static void
451test_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 */
481static void
482test_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
513static void
514test_index_to_x (void)
515{
516 PangoContext *context;
517 const char *tests[] = {
518 "ac­ual­ly", // soft hyphen
519 "ac​ual​ly", // 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
561static gboolean
562pango_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
571static void
572test_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
632static void
633test_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
688static void
689test_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
729static void
730test_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
767static void
768test_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 */
793static void
794test_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
821int
822main (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

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