/** * pango_log2vis_get_embedding_levels: * @text: the text to itemize. * @length: the number of bytes (not characters) to process, or -1 * if @text is nul-terminated and the length should be calculated. * @pbase_dir: input base direction, and output resolved direction. * * This will return the bidirectional embedding levels of the input paragraph * as defined by the Unicode Bidirectional Algorithm available at: * * http://www.unicode.org/reports/tr9/ * * If the input base direction is a weak direction, the direction of the * characters in the text will determine the final resolved direction. * * Return value: a newly allocated array of embedding levels, one item per * character (not byte), that should be freed using g_free. * * Since: 1.4 */ guint8 * pango_log2vis_get_embedding_levels (const gchar *text, int length, PangoDirection *pbase_dir) { glong n_chars, i; guint8 *embedding_levels_list; const gchar *p; FriBidiParType fribidi_base_dir; FriBidiCharType *bidi_types; #ifdef USE_FRIBIDI_EX_API FriBidiBracketType *bracket_types; #endif FriBidiLevel max_level; G_STATIC_ASSERT (sizeof (FriBidiLevel) == sizeof (guint8)); G_STATIC_ASSERT (sizeof (FriBidiChar) == sizeof (gunichar)); switch (*pbase_dir) { case PANGO_DIRECTION_LTR: case PANGO_DIRECTION_TTB_RTL: fribidi_base_dir = FRIBIDI_PAR_LTR; break; case PANGO_DIRECTION_RTL: case PANGO_DIRECTION_TTB_LTR: fribidi_base_dir = FRIBIDI_PAR_RTL; break; case PANGO_DIRECTION_WEAK_RTL: fribidi_base_dir = FRIBIDI_PAR_WRTL; break; case PANGO_DIRECTION_WEAK_LTR: case PANGO_DIRECTION_NEUTRAL: default: fribidi_base_dir = FRIBIDI_PAR_WLTR; break; } if (length < 0) length = strlen (text); n_chars = g_utf8_strlen (text, length); bidi_types = g_new (FriBidiCharType, n_chars); #ifdef USE_FRIBIDI_EX_API bracket_types = g_new (FriBidiBracketType, n_chars); #endif embedding_levels_list = g_new (guint8, n_chars); for (i = 0, p = text; p < text + length; p = g_utf8_next_char(p), i++) { gunichar ch = g_utf8_get_char (p); bidi_types[i] = fribidi_get_bidi_type (ch); #ifdef USE_FRIBIDI_EX_API if (G_UNLIKELY(bidi_types[i] == FRIBIDI_TYPE_ON)) bracket_types[i] = fribidi_get_bracket (ch); else bracket_types[i] = FRIBIDI_NO_BRACKET; #endif } #ifdef USE_FRIBIDI_EX_API max_level = fribidi_get_par_embedding_levels_ex (bidi_types, bracket_types, n_chars, &fribidi_base_dir, (FriBidiLevel*)embedding_levels_list); g_free (bracket_types); #else max_level = fribidi_get_par_embedding_levels (bidi_types, n_chars, &fribidi_base_dir, (FriBidiLevel*)embedding_levels_list); #endif g_free (bidi_types); if (G_UNLIKELY(max_level == 0)) { /* fribidi_get_par_embedding_levels() failed, * is this the best thing to do? */ memset (embedding_levels_list, 0, length); } *pbase_dir = (fribidi_base_dir == FRIBIDI_PAR_LTR) ? PANGO_DIRECTION_LTR : PANGO_DIRECTION_RTL; return embedding_levels_list; }
int main (int argc, char **argv) { GError *error; int next_arg; GIOChannel *channel; GIOStatus status; const char *filename; gchar *line = NULL; gsize length, terminator_pos; int numerrs = 0; int line_no = 0; FriBidiChar *code_points = NULL; int code_points_len = 0; int expected_ltor_len = 0; int base_dir_mode = 0, paragraph_dir; FriBidiLevel *expected_levels = NULL; int *expected_ltor = NULL; int resolved_paragraph_embedding_level; FriBidiLevel *levels = NULL; FriBidiCharType *types = NULL; FriBidiBracketType *bracket_types = NULL; FriBidiStrIndex *ltor = NULL; int ltor_len; gboolean debug = FALSE; if (argc < 2) { g_printerr ("usage: %s [--debug] test-file-name\n", argv[0]); exit (1); } next_arg = 1; while(next_arg < argc && argv[next_arg][0]=='-') { const char *arg = argv[next_arg++]; if (strcmp(arg, "--debug")==0) { debug=TRUE; continue; } die("Unknown option %s!\n", arg); } filename = argv[next_arg++]; error = NULL; channel = g_io_channel_new_file (filename, "r", &error); if (!channel) { g_printerr ("%s\n", error->message); exit (1); } fribidi_set_debug(debug); while (TRUE) { error = NULL; g_free (line); status = g_io_channel_read_line (channel, &line, &length, &terminator_pos, &error); switch (status) { case G_IO_STATUS_ERROR: g_printerr ("%s\n", error->message); exit (1); case G_IO_STATUS_EOF: goto done; case G_IO_STATUS_AGAIN: continue; case G_IO_STATUS_NORMAL: line[terminator_pos] = '\0'; break; } line_no++; if (line[0] == '#' || line[0] == '\0') continue; parse_test_line (line, line_no, &code_points, /* Field 0 */ &code_points_len, ¶graph_dir, /* Field 1 */ &resolved_paragraph_embedding_level, /* Field 2 */ &expected_levels, /* Field 3 */ &expected_ltor, /* Field 4 */ &expected_ltor_len ); /* Test it */ g_free(bracket_types); bracket_types = g_malloc ( sizeof(FriBidiBracketType) * code_points_len); g_free(types); types = g_malloc ( sizeof(FriBidiCharType) * code_points_len); g_free(levels); levels = g_malloc (sizeof (FriBidiLevel) * code_points_len); g_free (ltor); ltor = g_malloc (sizeof (FriBidiStrIndex) * code_points_len); { FriBidiParType base_dir; int i, j; gboolean matches; int types_len = code_points_len; int levels_len = types_len; FriBidiBracketType NoBracket = FRIBIDI_NO_BRACKET; for (i=0; i<code_points_len; i++) { types[i] = fribidi_get_bidi_type(code_points[i]); /* Note the optimization that a bracket is always of type neutral */ if (types[i] == FRIBIDI_TYPE_ON) bracket_types[i] = fribidi_get_bracket(code_points[i]); else bracket_types[i] = NoBracket; } if ((paragraph_dir & (1<<base_dir_mode)) == 0) continue; switch (paragraph_dir) { case 0: base_dir = FRIBIDI_PAR_LTR; break; case 1: base_dir = FRIBIDI_PAR_RTL; break; case 2: base_dir = FRIBIDI_PAR_ON; break; } if (fribidi_get_par_embedding_levels_ex (types, bracket_types, types_len, &base_dir, levels)) ; for (i = 0; i < types_len; i++) ltor[i] = i; if (fribidi_reorder_line (0 /*FRIBIDI_FLAG_REORDER_NSM*/, types, types_len, 0, base_dir, levels, NULL, ltor)) ; j = 0; for (i = 0; i < types_len; i++) if (!FRIBIDI_IS_EXPLICIT_OR_BN (types[ltor[i]])) ltor[j++] = ltor[i]; ltor_len = j; /* Compare */ matches = TRUE; if (matches) for (i = 0; i < code_points_len; i++) if (levels[i] != expected_levels[i] && expected_levels[i] != (FriBidiLevel) -1) { matches = FALSE; break; } if (ltor_len != expected_ltor_len) matches = FALSE; if (matches) for (i = 0; i < ltor_len; i++) if (ltor[i] != expected_ltor[i]) { matches = FALSE; break; } if (!matches) { numerrs++; g_printerr ("failure on line %d\n", line_no); g_printerr ("input is: %s\n", line); g_printerr ("base dir: %s\n", paragraph_dir==0 ? "LTR" : paragraph_dir==1 ? "RTL" : "AUTO"); g_printerr ("expected levels:"); for (i = 0; i < code_points_len; i++) if (expected_levels[i] == (FriBidiLevel) -1) g_printerr (" x"); else g_printerr (" %d", expected_levels[i]); g_printerr ("\n"); g_printerr ("returned levels:"); for (i = 0; i < levels_len; i++) g_printerr (" %d", levels[i]); g_printerr ("\n"); g_printerr ("expected order:"); for (i = 0; i < expected_ltor_len; i++) g_printerr (" %d", expected_ltor[i]); g_printerr ("\n"); g_printerr ("returned order:"); for (i = 0; i < ltor_len; i++) g_printerr (" %d", ltor[i]); g_printerr ("\n"); if (debug) { FriBidiParType base_dir; fribidi_set_debug (1); switch (base_dir_mode) { case 0: base_dir = FRIBIDI_PAR_ON; break; case 1: base_dir = FRIBIDI_PAR_LTR; break; case 2: base_dir = FRIBIDI_PAR_RTL; break; } if (fribidi_get_par_embedding_levels_ex (types, bracket_types, types_len, &base_dir, levels)) ; fribidi_set_debug (0); } g_printerr ("\n"); } } } done: if (error) g_error_free (error); if (numerrs) g_printerr ("%d errors\n", numerrs); else printf("No errors found! :-)\n"); return numerrs; }