1/* Pango
2 * testiter.c: Test pangolayoutiter.c
3 *
4 * Copyright (C) 2005 Amit Aronovitch
5 * Copyright (C) 2005 Red Hat, Inc
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
16 *
17 * You should have received a copy of the GNU Library General Public
18 * License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 * Boston, MA 02111-1307, USA.
21 */
22
23#undef G_DISABLE_ASSERT
24#undef G_LOG_DOMAIN
25
26#include <stdarg.h>
27#include <stdio.h>
28#include <stdlib.h>
29#include <string.h>
30
31#include <glib.h>
32
33#include <pango/pangocairo.h>
34
35static void verbose (const char *format, ...) G_GNUC_PRINTF (1, 2);
36static void
37verbose (const char *format, ...)
38{
39#ifdef VERBOSE
40 va_list vap;
41
42 va_start (vap, format);
43 vfprintf (stderr, format, vap);
44 va_end (vap);
45#endif
46}
47
48#define LAYOUT_WIDTH (80 * PANGO_SCALE)
49
50/* Note: The test expects that any newline sequence is of length 1
51 * use \n (not \r\n) in the test texts.
52 * I think the iterator itself should support \r\n without trouble,
53 * but there are comments in layout-iter.c suggesting otherwise.
54 */
55const char *test_texts[] =
56 {
57 /* English with embedded RTL runs (from ancient-hebrew.org) */
58 "The Hebrew word \xd7\x90\xd7\x93\xd7\x9d\xd7\x94 (adamah) is the feminine form of \xd7\x90\xd7\x93\xd7\x9d meaning \"ground\"\n",
59 /* Arabic, with vowel marks (from Sura Al Fatiha) */
60 "\xd8\xa8\xd9\x90\xd8\xb3\xd9\x92\xd9\x85\xd9\x90 \xd8\xa7\xd9\x84\xd9\x84\xd9\x91\xd9\x87\xd9\x90 \xd8\xa7\xd9\x84\xd8\xb1\xd9\x91\xd9\x8e\xd8\xad\xd9\x92\xd9\x85\xd9\x80\xd9\x8e\xd9\x86\xd9\x90 \xd8\xa7\xd9\x84\xd8\xb1\xd9\x91\xd9\x8e\xd8\xad\xd9\x90\xd9\x8a\xd9\x85\xd9\x90\n\xd8\xa7\xd9\x84\xd9\x92\xd8\xad\xd9\x8e\xd9\x85\xd9\x92\xd8\xaf\xd9\x8f \xd9\x84\xd9\x84\xd9\x91\xd9\x87\xd9\x90 \xd8\xb1\xd9\x8e\xd8\xa8\xd9\x91\xd9\x90 \xd8\xa7\xd9\x84\xd9\x92\xd8\xb9\xd9\x8e\xd8\xa7\xd9\x84\xd9\x8e\xd9\x85\xd9\x90\xd9\x8a\xd9\x86\xd9\x8e\n",
61 /* Arabic, with embedded LTR runs (from a Linux guide) */
62 "\xd8\xa7\xd9\x84\xd9\x85\xd8\xaa\xd8\xba\xd9\x8a\xd8\xb1 LC_ALL \xd9\x8a\xd8\xba\xd9\x8a\xd9\x8a\xd8\xb1 \xd9\x83\xd9\x84 \xd8\xa7\xd9\x84\xd9\x85\xd8\xaa\xd8\xba\xd9\x8a\xd8\xb1\xd8\xa7\xd8\xaa \xd8\xa7\xd9\x84\xd8\xaa\xd9\x8a \xd8\xaa\xd8\xa8\xd8\xaf\xd8\xa3 \xd8\xa8\xd8\xa7\xd9\x84\xd8\xb1\xd9\x85\xd8\xb2 LC.",
63 /* Hebrew, with vowel marks (from Genesis) */
64 "\xd7\x91\xd6\xbc\xd6\xb0\xd7\xa8\xd6\xb5\xd7\x90\xd7\xa9\xd7\x81\xd6\xb4\xd7\x99\xd7\xaa, \xd7\x91\xd6\xbc\xd6\xb8\xd7\xa8\xd6\xb8\xd7\x90 \xd7\x90\xd6\xb1\xd7\x9c\xd6\xb9\xd7\x94\xd6\xb4\xd7\x99\xd7\x9d, \xd7\x90\xd6\xb5\xd7\xaa \xd7\x94\xd6\xb7\xd7\xa9\xd6\xbc\xd7\x81\xd6\xb8\xd7\x9e\xd6\xb7\xd7\x99\xd6\xb4\xd7\x9d, \xd7\x95\xd6\xb0\xd7\x90\xd6\xb5\xd7\xaa \xd7\x94\xd6\xb8\xd7\x90\xd6\xb8\xd7\xa8\xd6\xb6\xd7\xa5",
65 /* Hebrew, with embedded LTR runs (from a Linux guide) */
66 "\xd7\x94\xd7\xa7\xd7\x9c\xd7\x93\xd7\x94 \xd7\xa2\xd7\x9c \xd7\xa9\xd7\xa0\xd7\x99 \xd7\x94 SHIFT\xd7\x99\xd7\x9d (\xd7\x99\xd7\x9e\xd7\x99\xd7\x9f \xd7\x95\xd7\xa9\xd7\x9e\xd7\x90\xd7\x9c \xd7\x91\xd7\x99\xd7\x97\xd7\x93) \xd7\x90\xd7\x9e\xd7\x95\xd7\xa8\xd7\x99\xd7\x9d \xd7\x9c\xd7\x94\xd7\x93\xd7\x9c\xd7\x99\xd7\xa7 \xd7\x90\xd7\xaa \xd7\xa0\xd7\x95\xd7\xa8\xd7\xaa \xd7\x94 Scroll Lock , \xd7\x95\xd7\x9c\xd7\x94\xd7\xa2\xd7\x91\xd7\x99\xd7\xa8 \xd7\x90\xd7\x95\xd7\xaa\xd7\xa0\xd7\x95 \xd7\x9c\xd7\x9e\xd7\xa6\xd7\x91 \xd7\x9b\xd7\xaa\xd7\x99\xd7\x91\xd7\x94 \xd7\x91\xd7\xa2\xd7\x91\xd7\xa8\xd7\x99\xd7\xaa.",
67 /* Different line terminators */
68 "AAAA\nBBBB\nCCCC\n",
69 "DDDD\rEEEE\rFFFF\r",
70 "GGGG\r\nHHHH\r\nIIII\r\n",
71 "asdf",
72 NULL
73 };
74
75/* char iteration test:
76 * - Total num of iterations match number of chars
77 * - GlyphString's index_to_x positions match those returned by the Iter
78 */
79static void
80iter_char_test (PangoLayout *layout)
81{
82 PangoRectangle extents, run_extents;
83 PangoLayoutIter *iter;
84 PangoLayoutRun *run;
85 int num_chars;
86 int i, index, offset;
87 int leading_x, trailing_x, x0, x1;
88 gboolean iter_next_ok, rtl;
89 const char *text, *ptr;
90
91 text = pango_layout_get_text (layout);
92 num_chars = g_utf8_strlen (p: text, max: -1);
93
94 iter = pango_layout_get_iter (layout);
95 iter_next_ok = TRUE;
96
97 for (i = 0 ; i < num_chars; ++i)
98 {
99 gchar *char_str;
100 g_assert (iter_next_ok);
101
102 index = pango_layout_iter_get_index (iter);
103 ptr = text + index;
104 char_str = g_strndup (str: ptr, g_utf8_next_char (ptr) - ptr);
105 verbose (format: "i=%d (visual), index = %d '%s':\n",
106 i, index, char_str);
107 g_free (mem: char_str);
108
109 pango_layout_iter_get_char_extents (iter, logical_rect: &extents);
110 verbose (format: " char extents: x=%d,y=%d w=%d,h=%d\n",
111 extents.x, extents.y,
112 extents.width, extents.height);
113
114 run = pango_layout_iter_get_run (iter);
115
116 if (run)
117 {
118 PangoFontDescription *desc;
119 char *str;
120
121 /* Get needed data for the GlyphString */
122 pango_layout_iter_get_run_extents(iter, NULL, logical_rect: &run_extents);
123 offset = run->item->offset;
124 rtl = run->item->analysis.level%2;
125 desc = pango_font_describe (font: run->item->analysis.font);
126 str = pango_font_description_to_string (desc);
127 verbose (format: " (current run: font=%s,offset=%d,x=%d,len=%d,rtl=%d)\n",
128 str, offset, run_extents.x, run->item->length, rtl);
129 g_free (mem: str);
130 pango_font_description_free (desc);
131
132 /* Calculate expected x result using index_to_x */
133 pango_glyph_string_index_to_x (glyphs: run->glyphs,
134 text: (char *)(text + offset), length: run->item->length,
135 analysis: &run->item->analysis,
136 index_: index - offset, FALSE, x_pos: &leading_x);
137 pango_glyph_string_index_to_x (glyphs: run->glyphs,
138 text: (char *)(text + offset), length: run->item->length,
139 analysis: &run->item->analysis,
140 index_: index - offset, TRUE, x_pos: &trailing_x);
141
142 x0 = run_extents.x + MIN (leading_x, trailing_x);
143 x1 = run_extents.x + MAX (leading_x, trailing_x);
144
145 verbose (format: " (index_to_x ind=%d: expected x=%d, width=%d)\n",
146 index - offset, x0, x1 - x0);
147
148 g_assert (extents.x == x0);
149 g_assert (extents.width == x1 - x0);
150 }
151 else
152 {
153 /* We're on a line terminator */
154 }
155
156 iter_next_ok = pango_layout_iter_next_char (iter);
157 verbose (format: "more to go? %d\n", iter_next_ok);
158 }
159
160 /* There should be one character position iterator for each character in the
161 * input string */
162 g_assert (!iter_next_ok);
163
164 pango_layout_iter_free (iter);
165}
166
167static void
168iter_cluster_test (PangoLayout *layout)
169{
170 PangoRectangle extents;
171 PangoLayoutIter *iter;
172 int index;
173 gboolean iter_next_ok;
174 PangoLayoutLine *last_line = NULL;
175 int expected_next_x = 0;
176
177 iter = pango_layout_get_iter (layout);
178 iter_next_ok = TRUE;
179
180 while (iter_next_ok)
181 {
182 PangoLayoutLine *line = pango_layout_iter_get_line (iter);
183
184 /* Every cluster is part of a run */
185 g_assert (pango_layout_iter_get_run (iter));
186
187 index = pango_layout_iter_get_index (iter);
188
189 pango_layout_iter_get_cluster_extents (iter, NULL, logical_rect: &extents);
190
191 iter_next_ok = pango_layout_iter_next_cluster (iter);
192
193 verbose (format: "index = %d:\n", index);
194 verbose (format: " cluster extents: x=%d,y=%d w=%d,h=%d\n",
195 extents.x, extents.y,
196 extents.width, extents.height);
197 verbose (format: "more to go? %d\n", iter_next_ok);
198
199 /* All the clusters on a line should be next to each other and occupy
200 * the entire line. They advance linearly from left to right */
201 g_assert (extents.width >= 0);
202
203 if (last_line == line)
204 g_assert (extents.x == expected_next_x);
205
206 expected_next_x = extents.x + extents.width;
207
208 last_line = line;
209 }
210
211 g_assert (!iter_next_ok);
212
213 pango_layout_iter_free (iter);
214}
215
216static void
217test_layout_iter (void)
218{
219 const char **ptext;
220 PangoFontMap *fontmap;
221 PangoContext *context;
222 PangoFontDescription *font_desc;
223 PangoLayout *layout;
224
225 fontmap = pango_cairo_font_map_get_default ();
226 context = pango_font_map_create_context (fontmap);
227 font_desc = pango_font_description_from_string (str: "cantarell 11");
228 pango_context_set_font_description (context, desc: font_desc);
229
230 layout = pango_layout_new (context);
231 pango_layout_set_width (layout, LAYOUT_WIDTH);
232
233 for (ptext = test_texts; *ptext != NULL; ++ptext)
234 {
235 verbose (format: "--------- checking next text ----------\n");
236 verbose (format: " <%s>\n", *ptext);
237 verbose ( format: "len=%ld, bytes=%ld\n",
238 (long)g_utf8_strlen (p: *ptext, max: -1), (long)strlen (s: *ptext));
239
240 pango_layout_set_text (layout, text: *ptext, length: -1);
241 iter_char_test (layout);
242 iter_cluster_test (layout);
243 }
244
245 g_object_unref (object: layout);
246 g_object_unref (object: context);
247 pango_font_description_free (desc: font_desc);
248}
249
250static void
251test_glyphitem_iter (void)
252{
253 PangoFontMap *fontmap;
254 PangoContext *context;
255 PangoFontDescription *font_desc;
256 PangoLayout *layout;
257 PangoLayoutLine *line;
258 const char *text;
259 GSList *l;
260
261 fontmap = pango_cairo_font_map_get_default ();
262 context = pango_font_map_create_context (fontmap);
263 font_desc = pango_font_description_from_string (str: "cantarell 11");
264 pango_context_set_font_description (context, desc: font_desc);
265
266 layout = pango_layout_new (context);
267 /* This shouldn't form any ligatures. */
268 pango_layout_set_text (layout, text: "test تست", length: -1);
269 text = pango_layout_get_text (layout);
270
271 line = pango_layout_get_line (layout, line: 0);
272 for (l = line->runs; l; l = l->next)
273 {
274 PangoGlyphItem *run = l->data;
275 int direction;
276
277 for (direction = 0; direction < 2; direction++)
278 {
279 PangoGlyphItemIter iter;
280 gboolean have_cluster;
281 PangoGlyphItemIter *iter2;
282
283 for (have_cluster = direction ?
284 pango_glyph_item_iter_init_start (iter: &iter, glyph_item: run, text) :
285 pango_glyph_item_iter_init_end (iter: &iter, glyph_item: run, text);
286 have_cluster;
287 have_cluster = direction ?
288 pango_glyph_item_iter_next_cluster (iter: &iter) :
289 pango_glyph_item_iter_prev_cluster (iter: &iter))
290 {
291 verbose (format: "start index %d end index %d\n", iter.start_index, iter.end_index);
292 g_assert_true (iter.start_index < iter.end_index);
293 g_assert_true (iter.start_index + 2 >= iter.end_index);
294 g_assert_true (iter.start_char + 1 == iter.end_char);
295
296 iter2 = pango_glyph_item_iter_copy (orig: &iter);
297 g_assert_true (iter2->start_glyph == iter.start_glyph);
298 g_assert_true (iter2->start_index == iter.start_index);
299 g_assert_true (iter2->start_char == iter.start_char);
300 g_assert_true (iter2->end_glyph == iter.end_glyph);
301 g_assert_true (iter2->end_index == iter.end_index);
302 g_assert_true (iter2->end_char == iter.end_char);
303 pango_glyph_item_iter_free (iter: iter2);
304 }
305 }
306 }
307
308 g_object_unref (object: layout);
309 g_object_unref (object: context);
310 pango_font_description_free (desc: font_desc);
311}
312
313int
314main (int argc, char *argv[])
315{
316 g_test_init (argc: &argc, argv: &argv, NULL);
317
318 g_test_add_func (testpath: "/layout/iter", test_func: test_layout_iter);
319 g_test_add_func (testpath: "/layout/glyphitem-iter", test_func: test_glyphitem_iter);
320
321 return g_test_run ();
322}
323

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