Blob Blame History Raw
/* Pango
 * test-layout.c: Test Pango Layout
 *
 * Copyright (C) 2014 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.
 */

#include <glib.h>
#include <string.h>
#include <locale.h>

#ifndef G_OS_WIN32
#include <unistd.h>
#endif

#include "config.h"
#include <pango/pangocairo.h>
#include "test-common.h"


static PangoContext *context;


static const gchar *
enum_value_nick (GType type, gint value)
{
  GEnumClass *eclass;
  GEnumValue *ev;

  eclass = g_type_class_ref (type);
  ev = g_enum_get_value (eclass, value);
  g_type_class_unref (eclass);

  if (ev)
    return ev->value_nick;
  else
    return "?";
}

static const gchar *
direction_name (PangoDirection dir)
{
  return enum_value_nick (PANGO_TYPE_DIRECTION, dir);
}

static const gchar *
gravity_name (PangoGravity gravity)
{
  return enum_value_nick (PANGO_TYPE_GRAVITY, gravity);
}

static const gchar *
script_name (PangoScript script)
{
  return enum_value_nick (PANGO_TYPE_SCRIPT, script);
}

static gchar *
font_name (PangoFont *font)
{
  PangoFontDescription *desc;
  gchar *name;

  desc = pango_font_describe (font);
  name = pango_font_description_to_string (desc);
  pango_font_description_free (desc);

  return name;
}

static void
dump_lines (PangoLayout *layout, GString *string)
{
  PangoLayoutIter *iter;
  const gchar *text;
  gint index, index2;
  gboolean has_more;
  gchar *char_str;
  gint i;
  PangoLayoutLine *line;

  text = pango_layout_get_text (layout);
  iter = pango_layout_get_iter (layout);

  has_more = TRUE;
  index = pango_layout_iter_get_index (iter);
  index2 = 0;
  i = 0;
  while (has_more)
    {
      line = pango_layout_iter_get_line (iter);
      has_more = pango_layout_iter_next_line (iter);
      i++;

      if (has_more)
        {
          index2 = pango_layout_iter_get_index (iter);
          char_str = g_strndup (text + index, index2 - index);
        }
      else
        {
          char_str = g_strdup (text + index);
        }

      g_string_append_printf (string, "i=%d, index=%d, paragraph-start=%d, dir=%s '%s'\n",
                              i, index, line->is_paragraph_start, direction_name (line->resolved_dir),
                              char_str);
      g_free (char_str);

      index = index2;
    }
  pango_layout_iter_free (iter);
}

static void
dump_runs (PangoLayout *layout, GString *string)
{
  PangoLayoutIter *iter;
  PangoLayoutRun *run;
  PangoItem *item;
  const gchar *text;
  gint index, index2;
  gboolean has_more;
  gchar *char_str;
  gint i;
  gchar *font = 0;

  text = pango_layout_get_text (layout);
  iter = pango_layout_get_iter (layout);

  has_more = TRUE;
  index = pango_layout_iter_get_index (iter);
  index2 = 0;
  i = 0;
  while (has_more)
    {
      run = pango_layout_iter_get_run (iter);
      has_more = pango_layout_iter_next_run (iter);
      i++;

      if (has_more)
        {
          index2 = pango_layout_iter_get_index (iter);
          char_str = g_strndup (text + index, index2 - index);
        }
      else
        {
          char_str = g_strdup (text + index);
        }

      if (run)
        {
          item = ((PangoGlyphItem*)run)->item;
          font = font_name (item->analysis.font);
          g_string_append_printf (string, "i=%d, index=%d, chars=%d, level=%d, gravity=%s, flags=%d, font=%s, script=%s, language=%s, '%s'\n",
                                  i, index, item->num_chars, item->analysis.level,
                                  gravity_name (item->analysis.gravity),
                                  item->analysis.flags,
                                  "OMITTED", /* for some reason, this fails on build.gnome.org, so leave it out */
                                  script_name (item->analysis.script),
                                  pango_language_to_string (item->analysis.language),
                                  char_str);
          print_attributes (item->analysis.extra_attrs, string);
          g_free (font);
        }
      else
        {
          g_string_append_printf (string, "i=%d, index=%d, no run, line end\n",
                                  i, index);
        }

      g_free (char_str);
      index = index2;
    }
  pango_layout_iter_free (iter);
}

static void
parse_params (const gchar        *str,
              gint               *width,
              gint               *ellipsize_at,
              PangoEllipsizeMode *ellipsize,
              PangoWrapMode      *wrap)
{
  gchar **strings;
  gchar **str2;
  gint i;
  GEnumClass *eclass;
  GEnumValue *ev;

  strings = g_strsplit (str, ",", -1);
  for (i = 0; strings[i]; i++)
    {
      str2 = g_strsplit (strings[i], "=", -1);
      if (strcmp (str2[0], "width") == 0)
        *width = (gint) g_ascii_strtoll (str2[1], NULL, 10);
      else if (strcmp (str2[0], "ellipsize-at") == 0)
        *ellipsize_at = (gint) g_ascii_strtoll (str2[1], NULL, 10);
      else if (strcmp (str2[0], "ellipsize") == 0)
        {
          eclass = g_type_class_ref (PANGO_TYPE_ELLIPSIZE_MODE);
          ev = g_enum_get_value_by_name (eclass, str2[1]);
          if (!ev)
            ev = g_enum_get_value_by_nick (eclass, str2[1]);
          if (ev)
            *ellipsize = ev->value;
          g_type_class_unref (eclass);
        }
      else if (strcmp (str2[0], "wrap") == 0)
        {
          eclass = g_type_class_ref (PANGO_TYPE_WRAP_MODE);
          ev = g_enum_get_value_by_name (eclass, str2[1]);
          if (!ev)
            ev = g_enum_get_value_by_nick (eclass, str2[1]);
          if (ev)
            *wrap = ev->value;
          g_type_class_unref (eclass);
        }
      g_strfreev (str2);
    }
  g_strfreev (strings);
}

static void
test_file (const gchar *filename, GString *string)
{
  gchar *contents;
  gchar *markup;
  gsize  length;
  GError *error = NULL;
  PangoLayout *layout;
  gchar *p;
  gint width = 0;
  gint ellipsize_at = 0;
  PangoEllipsizeMode ellipsize = PANGO_ELLIPSIZE_NONE;
  PangoWrapMode wrap = PANGO_WRAP_WORD;
  PangoFontDescription *desc;

  if (!g_file_get_contents (filename, &contents, &length, &error))
    {
      fprintf (stderr, "%s\n", error->message);
      g_error_free (error);
      return;
    }

  p = strchr (contents, '\n');
  g_assert (p);
  markup = p + 1;
  *p = '\0';

  parse_params (contents, &width, &ellipsize_at, &ellipsize, &wrap);

  layout = pango_layout_new (context);
  desc = pango_font_description_from_string ("Cantarell 11");
  pango_layout_set_font_description (layout, desc);
  pango_font_description_free (desc);

  pango_layout_set_markup (layout, markup, length);
  g_free (contents);

  if (width != 0)
    pango_layout_set_width (layout, width * PANGO_SCALE);
  pango_layout_set_ellipsize (layout, ellipsize);
  pango_layout_set_wrap (layout, wrap);

  g_string_append (string, pango_layout_get_text (layout));
  g_string_append (string, "\n---\n\n");
  g_string_append_printf (string, "wrapped: %d\n", pango_layout_is_wrapped (layout));
  g_string_append_printf (string, "ellipsized: %d\n", pango_layout_is_ellipsized (layout));
  g_string_append_printf (string, "lines: %d\n", pango_layout_get_line_count (layout));
  if (width != 0)
    g_string_append_printf (string, "width: %d\n", pango_layout_get_width (layout));
  g_string_append (string, "\n---\n\n");
  print_attr_list (pango_layout_get_attributes (layout), string);
  g_string_append (string, "\n---\n\n");
  dump_lines (layout, string);
  g_string_append (string, "\n---\n\n");
  dump_runs (layout, string);

  g_object_unref (layout);
}

static gchar *
get_expected_filename (const gchar *filename)
{
  gchar *f, *p, *expected;

  f = g_strdup (filename);
  p = strstr (f, ".markup");
  if (p)
    *p = 0;
  expected = g_strconcat (f, ".expected", NULL);

  g_free (f);

  return expected;
}

static void
test_layout (gconstpointer d)
{
  const gchar *filename = d;
  gchar *expected_file;
  GError *error = NULL;
  GString *dump;
  gchar *diff;

  expected_file = get_expected_filename (filename);

  dump = g_string_sized_new (0);

  test_file (filename, dump);

  diff = diff_with_file (expected_file, dump->str, dump->len, &error);
  g_assert_no_error (error);

  if (diff && diff[0])
    {
      g_printerr ("Contents don't match expected contents:\n%s", diff);
      g_test_fail ();
      g_free (diff);
    }

  g_string_free (dump, TRUE);
  g_free (expected_file);
}

int
main (int argc, char *argv[])
{
  GDir *dir;
  GError *error = NULL;
  const gchar *name;
  gchar *path;

  g_setenv ("LC_ALL", "C", TRUE);
  setlocale (LC_ALL, "");

  g_test_init (&argc, &argv, NULL);

  context = pango_font_map_create_context (pango_cairo_font_map_get_default ());

  /* allow to easily generate expected output for new test cases */
  if (argc > 1)
    {
      GString *string;

      string = g_string_sized_new (0);
      test_file (argv[1], string);
      g_print ("%s", string->str);

      return 0;
    }

  path = g_test_build_filename (G_TEST_DIST, "layouts", NULL);
  dir = g_dir_open (path, 0, &error);
  g_free (path);
  g_assert_no_error (error);
  while ((name = g_dir_read_name (dir)) != NULL)
    {
      if (!strstr (name, "markup"))
        continue;

      path = g_strdup_printf ("/layout/%s", name);
      g_test_add_data_func_full (path, g_test_build_filename (G_TEST_DIST, "layouts", name, NULL),
                                 test_layout, g_free);
      g_free (path);
    }
  g_dir_close (dir);

  return g_test_run ();
}