/* Pango
* testiter.c: Test pangolayoutiter.c
*
* Copyright (C) 2005 Amit Aronovitch
* Copyright (C) 2005 Red Hat, Inc
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#undef G_DISABLE_ASSERT
#undef G_LOG_DOMAIN
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <glib.h>
#include <pango/pangocairo.h>
static void verbose (const char *format, ...) G_GNUC_PRINTF (1, 2);
static void
verbose (const char *format, ...)
{
#ifdef VERBOSE
va_list vap;
va_start (vap, format);
vfprintf (stderr, format, vap);
va_end (vap);
#endif
}
#define LAYOUT_WIDTH (80 * PANGO_SCALE)
/* Note: The test expects that any newline sequence is of length 1
* use \n (not \r\n) in the test texts.
* I think the iterator itself should support \r\n without trouble,
* but there are comments in layout-iter.c suggesting otherwise.
*/
const char *test_texts[] =
{
/* English with embedded RTL runs (from ancient-hebrew.org) */
"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",
/* Arabic, with vowel marks (from Sura Al Fatiha) */
"\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",
/* Arabic, with embedded LTR runs (from a Linux guide) */
"\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.",
/* Hebrew, with vowel marks (from Genesis) */
"\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",
/* Hebrew, with embedded LTR runs (from a Linux guide) */
"\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.",
/* Different line terminators */
"AAAA\nBBBB\nCCCC\n",
"DDDD\rEEEE\rFFFF\r",
"GGGG\r\nHHHH\r\nIIII\r\n",
"asdf",
NULL
};
/* char iteration test:
* - Total num of iterations match number of chars
* - GlyphString's index_to_x positions match those returned by the Iter
*/
static void
iter_char_test (PangoLayout *layout)
{
PangoRectangle extents, run_extents;
PangoLayoutIter *iter;
PangoLayoutRun *run;
int num_chars;
int i, index, offset;
int leading_x, trailing_x, x0, x1;
gboolean iter_next_ok, rtl;
const char *text, *ptr;
text = pango_layout_get_text (layout);
num_chars = g_utf8_strlen (text, -1);
iter = pango_layout_get_iter (layout);
iter_next_ok = TRUE;
for (i = 0 ; i < num_chars; ++i)
{
gchar *char_str;
g_assert (iter_next_ok);
index = pango_layout_iter_get_index (iter);
ptr = text + index;
char_str = g_strndup (ptr, g_utf8_next_char (ptr) - ptr);
verbose ("i=%d (visual), index = %d '%s':\n",
i, index, char_str);
g_free (char_str);
pango_layout_iter_get_char_extents (iter, &extents);
verbose (" char extents: x=%d,y=%d w=%d,h=%d\n",
extents.x, extents.y,
extents.width, extents.height);
run = pango_layout_iter_get_run (iter);
if (run)
{
/* Get needed data for the GlyphString */
pango_layout_iter_get_run_extents(iter, NULL, &run_extents);
offset = run->item->offset;
rtl = run->item->analysis.level%2;
verbose (" (current run: offset=%d,x=%d,len=%d,rtl=%d)\n",
offset, run_extents.x, run->item->length, rtl);
/* Calculate expected x result using index_to_x */
pango_glyph_string_index_to_x (run->glyphs,
(char *)(text + offset), run->item->length,
&run->item->analysis,
index - offset, FALSE, &leading_x);
pango_glyph_string_index_to_x (run->glyphs,
(char *)(text + offset), run->item->length,
&run->item->analysis,
index - offset, TRUE, &trailing_x);
x0 = run_extents.x + MIN (leading_x, trailing_x);
x1 = run_extents.x + MAX (leading_x, trailing_x);
verbose (" (index_to_x ind=%d: expected x=%d, width=%d)\n",
index - offset, x0, x1 - x0);
g_assert (extents.x == x0);
g_assert (extents.width == x1 - x0);
}
else
{
/* We're on a line terminator */
}
iter_next_ok = pango_layout_iter_next_char (iter);
verbose ("more to go? %d\n", iter_next_ok);
}
/* There should be one character position iterator for each character in the
* input string */
g_assert (!iter_next_ok);
pango_layout_iter_free (iter);
}
static void
iter_cluster_test (PangoLayout *layout)
{
PangoRectangle extents;
PangoLayoutIter *iter;
int index;
gboolean iter_next_ok;
PangoLayoutLine *last_line = NULL;
int expected_next_x = 0;
iter = pango_layout_get_iter (layout);
iter_next_ok = TRUE;
while (iter_next_ok)
{
PangoLayoutLine *line = pango_layout_iter_get_line (iter);
/* Every cluster is part of a run */
g_assert (pango_layout_iter_get_run (iter));
index = pango_layout_iter_get_index (iter);
pango_layout_iter_get_cluster_extents (iter, NULL, &extents);
iter_next_ok = pango_layout_iter_next_cluster (iter);
verbose ("index = %d:\n", index);
verbose (" cluster extents: x=%d,y=%d w=%d,h=%d\n",
extents.x, extents.y,
extents.width, extents.height);
verbose ("more to go? %d\n", iter_next_ok);
/* All the clusters on a line should be next to each other and occupy
* the entire line. They advance linearly from left to right */
g_assert (extents.width >= 0);
if (last_line == line)
g_assert (extents.x == expected_next_x);
expected_next_x = extents.x + extents.width;
last_line = line;
}
g_assert (!iter_next_ok);
pango_layout_iter_free (iter);
}
static void
test_layout_iter (void)
{
const char **ptext;
PangoFontMap *fontmap;
PangoContext *context;
PangoFontDescription *font_desc;
PangoLayout *layout;
fontmap = pango_cairo_font_map_get_default ();
context = pango_font_map_create_context (fontmap);
font_desc = pango_font_description_from_string ("cantarell 11");
pango_context_set_font_description (context, font_desc);
layout = pango_layout_new (context);
pango_layout_set_width (layout, LAYOUT_WIDTH);
for (ptext = test_texts; *ptext != NULL; ++ptext)
{
verbose ("--------- checking next text ----------\n");
verbose (" <%s>\n", *ptext);
verbose ( "len=%ld, bytes=%ld\n",
(long)g_utf8_strlen (*ptext, -1), (long)strlen (*ptext));
pango_layout_set_text (layout, *ptext, -1);
iter_char_test (layout);
iter_cluster_test (layout);
}
g_object_unref (layout);
g_object_unref (context);
pango_font_description_free (font_desc);
}
static void
test_glyphitem_iter (void)
{
PangoFontMap *fontmap;
PangoContext *context;
PangoFontDescription *font_desc;
PangoLayout *layout;
PangoLayoutLine *line;
const char *text;
GSList *l;
fontmap = pango_cairo_font_map_get_default ();
context = pango_font_map_create_context (fontmap);
font_desc = pango_font_description_from_string ("cantarell 11");
pango_context_set_font_description (context, font_desc);
layout = pango_layout_new (context);
/* This shouldn't form any ligatures. */
pango_layout_set_text (layout, "test تست", -1);
text = pango_layout_get_text (layout);
line = pango_layout_get_line (layout, 0);
for (l = line->runs; l; l = l->next)
{
PangoGlyphItem *run = l->data;
int direction;
for (direction = 0; direction < 2; direction++)
{
PangoGlyphItemIter iter;
gboolean have_cluster;
for (have_cluster = direction ?
pango_glyph_item_iter_init_start (&iter, run, text) :
pango_glyph_item_iter_init_end (&iter, run, text);
have_cluster;
have_cluster = direction ?
pango_glyph_item_iter_next_cluster (&iter) :
pango_glyph_item_iter_prev_cluster (&iter))
{
verbose ("start index %d end index %d\n", iter.start_index, iter.end_index);
g_assert (iter.start_index < iter.end_index);
g_assert (iter.start_index + 2 >= iter.end_index);
g_assert (iter.start_char + 1 == iter.end_char);
}
}
}
g_object_unref (layout);
g_object_unref (context);
pango_font_description_free (font_desc);
}
int
main (int argc, char *argv[])
{
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/layout/iter", test_layout_iter);
g_test_add_func ("/layout/glyphitem-iter", test_glyphitem_iter);
return g_test_run ();
}