static void get_bidi_levels(FriBidiChar *in,int length,int is_rtl,FriBidiLevel *embed) { FriBidiCharType *types = utl_malloc(sizeof(FriBidiCharType)*length); FriBidiParType direction = is_rtl ? FRIBIDI_PAR_RTL : FRIBIDI_PAR_LTR; fribidi_get_bidi_types(in,length,types); fribidi_get_par_embedding_levels(types,length,&direction,embed); utl_free(types); }
static int AnalyzeParagraph( paragraph_t *p_paragraph ) { fribidi_get_bidi_types( p_paragraph->p_code_points, p_paragraph->i_size, p_paragraph->p_types ); fribidi_get_par_embedding_levels( p_paragraph->p_types, p_paragraph->i_size, &p_paragraph->paragraph_type, p_paragraph->p_levels ); #ifdef HAVE_HARFBUZZ hb_unicode_funcs_t *p_funcs = hb_unicode_funcs_get_default(); for( int i = 0; i < p_paragraph->i_size; ++i ) p_paragraph->p_scripts[ i ] = hb_unicode_script( p_funcs, p_paragraph->p_code_points[ i ] ); hb_script_t i_last_script; int i_last_script_index = -1; int i_last_set_index = -1; /* * For shaping to work, characters that are assigned HB_SCRIPT_COMMON or * HB_SCRIPT_INHERITED should be resolved to the last encountered valid * script value, if any, and to the first one following them otherwise */ for( int i = 0; i < p_paragraph->i_size; ++i ) { if( p_paragraph->p_scripts[ i ] == HB_SCRIPT_COMMON || p_paragraph->p_scripts[ i ] == HB_SCRIPT_INHERITED) { if( i_last_script_index != -1) { p_paragraph->p_scripts[ i ] = i_last_script; i_last_set_index = i; } } else { for( int j = i_last_set_index + 1; j < i; ++j ) p_paragraph->p_scripts[ j ] = p_paragraph->p_scripts[ i ]; i_last_script = p_paragraph->p_scripts[ i ]; i_last_script_index = i; i_last_set_index = i; } } #endif //HAVE_HARFBUZZ return VLC_SUCCESS; }
int main (int argc, char **argv) { GIOChannel *channel; GIOStatus status; GError *error; gchar *line = NULL; gsize length, terminator_pos; FriBidiStrIndex *expected_ltor = NULL; FriBidiStrIndex expected_ltor_len = 0; FriBidiStrIndex *ltor = NULL; FriBidiStrIndex ltor_len = 0; FriBidiCharType *types = NULL; FriBidiStrIndex types_len = 0; FriBidiLevel *expected_levels = NULL; FriBidiLevel expected_levels_len = 0; FriBidiLevel *levels = NULL; FriBidiStrIndex levels_len = 0; int base_dir_flags, base_dir_mode; int numerrs = 0; int numtests = 0; int line_no = 0; gboolean debug = FALSE; const char *filename; int next_arg; if (argc < 2) { g_printerr ("usage: %s [--debug] test-file-name\n", argv[0]); exit (1); } next_arg = 1; if (!strcmp (argv[next_arg], "--debug")) { debug = TRUE; next_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); } 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; if (line[0] == '@') { if (!strncmp (line, "@Reorder:", 9)) { g_free (expected_ltor); expected_ltor = parse_reorder_line (line, &expected_ltor_len); continue; } if (!strncmp (line, "@Levels:", 8)) { g_free (expected_levels); expected_levels = parse_levels_line (line, &expected_levels_len); continue; } continue; } /* Test line */ g_free (types); types = parse_test_line (line, &types_len, &base_dir_flags); g_free (levels); levels = g_malloc (sizeof (FriBidiLevel) * types_len); levels_len = types_len; g_free (ltor); ltor = g_malloc (sizeof (FriBidiStrIndex) * types_len); /* Test it */ for (base_dir_mode = 0; base_dir_mode < 3; base_dir_mode++) { FriBidiParType base_dir; int i, j; gboolean matches; if ((base_dir_flags & (1<<base_dir_mode)) == 0) continue; numtests++; 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; } fribidi_get_par_embedding_levels (types, types_len, &base_dir, levels); for (i = 0; i < types_len; i++) ltor[i] = i; 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 (levels_len != expected_levels_len) matches = FALSE; if (matches) for (i = 0; i < levels_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", base_dir_mode==0 ? "auto" : base_dir_mode==1 ? "LTR" : "RTL"); g_printerr ("expected levels:"); for (i = 0; i < expected_levels_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; } fribidi_get_par_embedding_levels (types, types_len, &base_dir, levels); fribidi_set_debug (0); } g_printerr ("\n"); } } } done: g_free (ltor); g_free (levels); g_free (expected_ltor); g_free (types); g_free (line); g_io_channel_unref (channel); if (error) g_error_free (error); if (numerrs) g_printerr ("%d errors out of %d total tests\n", numerrs, numtests); return numerrs; }
/** * 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; }
static int shape_text(AVFilterContext *ctx) { DrawTextContext *s = ctx->priv; uint8_t *tmp; int ret = AVERROR(ENOMEM); static const FriBidiFlags flags = FRIBIDI_FLAGS_DEFAULT | FRIBIDI_FLAGS_ARABIC; FriBidiChar *unicodestr = NULL; FriBidiStrIndex len; FriBidiParType direction = FRIBIDI_PAR_LTR; FriBidiStrIndex line_start = 0; FriBidiStrIndex line_end = 0; FriBidiLevel *embedding_levels = NULL; FriBidiArabicProp *ar_props = NULL; FriBidiCharType *bidi_types = NULL; FriBidiStrIndex i,j; len = strlen(s->text); if (!(unicodestr = av_malloc_array(len, sizeof(*unicodestr)))) { goto out; } len = fribidi_charset_to_unicode(FRIBIDI_CHAR_SET_UTF8, s->text, len, unicodestr); bidi_types = av_malloc_array(len, sizeof(*bidi_types)); if (!bidi_types) { goto out; } fribidi_get_bidi_types(unicodestr, len, bidi_types); embedding_levels = av_malloc_array(len, sizeof(*embedding_levels)); if (!embedding_levels) { goto out; } if (!fribidi_get_par_embedding_levels(bidi_types, len, &direction, embedding_levels)) { goto out; } ar_props = av_malloc_array(len, sizeof(*ar_props)); if (!ar_props) { goto out; } fribidi_get_joining_types(unicodestr, len, ar_props); fribidi_join_arabic(bidi_types, len, embedding_levels, ar_props); fribidi_shape(flags, embedding_levels, len, ar_props, unicodestr); for (line_end = 0, line_start = 0; line_end < len; line_end++) { if (is_newline(unicodestr[line_end]) || line_end == len - 1) { if (!fribidi_reorder_line(flags, bidi_types, line_end - line_start + 1, line_start, direction, embedding_levels, unicodestr, NULL)) { goto out; } line_start = line_end + 1; } } /* Remove zero-width fill chars put in by libfribidi */ for (i = 0, j = 0; i < len; i++) if (unicodestr[i] != FRIBIDI_CHAR_FILL) unicodestr[j++] = unicodestr[i]; len = j; if (!(tmp = av_realloc(s->text, (len * 4 + 1) * sizeof(*s->text)))) { /* Use len * 4, as a unicode character can be up to 4 bytes in UTF-8 */ goto out; } s->text = tmp; len = fribidi_unicode_to_charset(FRIBIDI_CHAR_SET_UTF8, unicodestr, len, s->text); ret = 0; out: av_free(unicodestr); av_free(embedding_levels); av_free(ar_props); av_free(bidi_types); return ret; }
/** * @internal * Allocates bidi properties according to ustr. First checks to see if the * passed has rtl chars, if not, it returns NULL. * * Assumes all the segment_idxs are either -1 or legal, and > 0 indexes. * Also assumes that the characters at the override points are of weak/neutral * bidi type, otherwise unexpected results may occur. * * @param ustr The string to update according to. * @param len The length of the string * @param segment_idxs A -1 terminated array of points to start a new bidi analysis at (used for section high level bidi overrides). - NULL means none. * @return returns allocated paragraph props on success, NULL otherwise. */ Evas_BiDi_Paragraph_Props * evas_bidi_paragraph_props_get(const Eina_Unicode *eina_ustr, size_t len, int *segment_idxs) { Evas_BiDi_Paragraph_Props *bidi_props = NULL; EvasBiDiCharType *char_types = NULL; EvasBiDiLevel *embedding_levels = NULL; const FriBidiChar *ustr; FriBidiChar *base_ustr = NULL; if (!eina_ustr) return NULL; if (!evas_bidi_is_rtl_str(eina_ustr)) /* No need to handle bidi */ { len = -1; goto cleanup; } len = eina_unicode_strlen(eina_ustr); /* The size of fribidichar s different than eina_unicode, convert */ #ifdef EVAS_FRIBIDI_EINA_UNICODE_UNEQUAL base_ustr = calloc(len + 1, sizeof(FriBidiChar)); base_ustr = _evas_bidi_unicode_to_fribidichar(base_ustr, eina_ustr); ustr = base_ustr; #else ustr = (const FriBidiChar *) eina_ustr; #endif bidi_props = evas_bidi_paragraph_props_new(); /* Prep work for reordering */ char_types = (EvasBiDiCharType *) malloc(sizeof(EvasBiDiCharType) * len); if (!char_types) { len = -2; goto cleanup; } fribidi_get_bidi_types(ustr, len, char_types); embedding_levels = (EvasBiDiLevel *)malloc(sizeof(EvasBiDiLevel) * len); if (!embedding_levels) { len = -2; goto cleanup; } if (segment_idxs) { size_t pos = 0; int *itr; EvasBiDiLevel base_level = 0; EvasBiDiParType direction; for (itr = segment_idxs ; *itr > 0 ; itr++) { direction = EVAS_BIDI_PARAGRAPH_NEUTRAL; if (!fribidi_get_par_embedding_levels(char_types + pos, *itr - pos, &direction, embedding_levels + pos)) { len = -2; goto cleanup; } /* Only on the first run */ if (itr == segment_idxs) { bidi_props->direction = direction; /* adjust base_level to be 1 for rtl paragraphs, and 0 for * ltr paragraphs. */ base_level = EVAS_BIDI_PARAGRAPH_DIRECTION_IS_RTL(bidi_props) ? 1 : 0; } /* We want those chars at the override points to be on the base * level and we also remove -2 cause we later increment them, * just for simpler code paths */ embedding_levels[*itr] = base_level - 2; pos = *itr + 1; } direction = EVAS_BIDI_PARAGRAPH_NEUTRAL; if (!fribidi_get_par_embedding_levels(char_types + pos, len - pos, &direction, embedding_levels + pos)) { len = -2; goto cleanup; } /* Increment all levels by 2 to emulate embedding. */ { EvasBiDiLevel *bitr = embedding_levels, *end; end = bitr + len; for ( ; bitr < end ; bitr++) { *bitr += 2; } } } else { if (!fribidi_get_par_embedding_levels(char_types, len, &bidi_props->direction, embedding_levels)) { len = -2; goto cleanup; } } /* clean up */ if (bidi_props->embedding_levels) { free(bidi_props->embedding_levels); } bidi_props->embedding_levels = embedding_levels; /* clean up */ if (bidi_props->char_types) { free(bidi_props->char_types); } bidi_props->char_types = char_types; if (base_ustr) free(base_ustr); return bidi_props; /* Cleanup */ cleanup: if (char_types) free(char_types); if (embedding_levels) free(embedding_levels); if (base_ustr) free(base_ustr); if (bidi_props) evas_bidi_paragraph_props_unref(bidi_props); /* Clean up the bidi props */ return NULL; }
bool GlyphString::analyze(bool resolveScripts, bool breakOnLevelChange) { if (mState != Initialized) return false; fribidi_get_bidi_types(mCodePoints, mSize, mTypes); fribidi_get_par_embedding_levels(mTypes, mSize, &mParType, mLevels); hb_unicode_funcs_t *ufuncs = hb_unicode_funcs_get_default(); for (int i = 0; i < mSize; ++i) mScripts[i] = hb_unicode_script(ufuncs, mCodePoints[i]); if (resolveScripts) { hb_script_t lastScriptValue; int lastScriptIndex = -1; int lastSetIndex = -1; for (int i = 0; i < mSize; ++i) { if (mScripts[i] == HB_SCRIPT_COMMON || mScripts[i] == HB_SCRIPT_INHERITED) { if (lastScriptIndex != -1) { mScripts[i] = lastScriptValue; lastSetIndex = i; } } else { for (int j = lastSetIndex + 1; j < i; ++j) mScripts[j] = mScripts[i]; lastScriptValue = mScripts[i]; lastScriptIndex = i; lastSetIndex = i; } } } int runID = 0; hb_script_t lastScript = mScripts[0]; int lastLevel = mLevels[0]; int lastRunStart = 0; for (int i = 0; i <= mSize; ++i) { if (i == mSize || mScripts[i] != lastScript || (breakOnLevelChange && mLevels[i] != lastLevel)) { ++runID; RunInfo run; run.startOffset = lastRunStart; run.endOffset = i; run.script = lastScript; run.direction = lastLevel & 1 ? HB_DIRECTION_RTL : HB_DIRECTION_LTR; mRunInfos.push_back(run); if (i < mSize) { lastScript = mScripts[i]; lastLevel = mLevels[i]; lastRunStart = i; } else { break; } } mRuns[i] = runID; } mState = Analyzed; return true; }
static bool _raqm_itemize (raqm_t *rq) { FriBidiParType par_type = FRIBIDI_PAR_ON; #ifdef _MSC_VER FriBidiCharType *types = _alloca (rq->text_len); FriBidiLevel *levels = _alloca (rq->text_len); #else FriBidiCharType types[rq->text_len]; FriBidiLevel levels[rq->text_len]; #endif FriBidiRun *runs = NULL; raqm_run_t *last; int max_level; int run_count; bool ok = true; #ifdef RAQM_TESTING switch (rq->base_dir) { case RAQM_DIRECTION_RTL: RAQM_TEST ("Direction is: RTL\n\n"); break; case RAQM_DIRECTION_LTR: RAQM_TEST ("Direction is: LTR\n\n"); break; case RAQM_DIRECTION_TTB: RAQM_TEST ("Direction is: TTB\n\n"); break; case RAQM_DIRECTION_DEFAULT: default: RAQM_TEST ("Direction is: DEFAULT\n\n"); break; } #endif if (rq->base_dir == RAQM_DIRECTION_RTL) par_type = FRIBIDI_PAR_RTL; else if (rq->base_dir == RAQM_DIRECTION_LTR) par_type = FRIBIDI_PAR_LTR; if (rq->base_dir == RAQM_DIRECTION_TTB) { /* Treat every thing as LTR in vertical text */ max_level = 0; memset (types, FRIBIDI_TYPE_LTR, rq->text_len); memset (levels, 0, rq->text_len); } else { fribidi_get_bidi_types (rq->text, rq->text_len, types); max_level = fribidi_get_par_embedding_levels (types, rq->text_len, &par_type, levels); } if (max_level < 0) return false; if (!_raqm_resolve_scripts (rq)) return false; /* Get the number of bidi runs */ run_count = fribidi_reorder_runs (types, rq->text_len, par_type, levels, NULL); /* Populate bidi runs array */ runs = malloc (sizeof (FriBidiRun) * (size_t)run_count); if (!runs) return false; run_count = fribidi_reorder_runs (types, rq->text_len, par_type, levels, runs); #ifdef RAQM_TESTING RAQM_TEST ("Number of runs before script itemization: %d\n\n", run_count); RAQM_TEST ("Fribidi Runs:\n"); for (int i = 0; i < run_count; i++) { RAQM_TEST ("run[%d]:\t start: %d\tlength: %d\tlevel: %d\n", i, runs[i].pos, runs[i].len, runs[i].level); } RAQM_TEST ("\n"); #endif last = NULL; for (int i = 0; i < run_count; i++) { raqm_run_t *run = calloc (1, sizeof (raqm_run_t)); if (!run) return false; if (!rq->runs) rq->runs = run; if (last) last->next = run; run->direction = _raqm_hb_dir (rq, runs[i].level); if (HB_DIRECTION_IS_BACKWARD (run->direction)) { run->pos = runs[i].pos + runs[i].len - 1; run->script = rq->scripts[run->pos]; for (int j = runs[i].len - 1; j >= 0; j--) { hb_script_t script = rq->scripts[runs[i].pos + j]; if (script != run->script) { raqm_run_t *newrun = calloc (1, sizeof (raqm_run_t)); if (!newrun) return false; newrun->pos = runs[i].pos + j; newrun->len = 1; newrun->direction = _raqm_hb_dir (rq, runs[i].level); newrun->script = script; run->next = newrun; run = newrun; } else { run->len++; run->pos = runs[i].pos + j; } } } else { run->pos = runs[i].pos; run->script = rq->scripts[run->pos]; for (int j = 0; j < runs[i].len; j++) { hb_script_t script = rq->scripts[runs[i].pos + j]; if (script != run->script) { raqm_run_t *newrun = calloc (1, sizeof (raqm_run_t)); if (!newrun) return false; newrun->pos = runs[i].pos + j; newrun->len = 1; newrun->direction = _raqm_hb_dir (rq, runs[i].level); newrun->script = script; run->next = newrun; run = newrun; } else run->len++; } } last = run; last->next = NULL; } #ifdef RAQM_TESTING run_count = 0; for (raqm_run_t *run = rq->runs; run != NULL; run = run->next) run_count++; RAQM_TEST ("Number of runs after script itemization: %d\n\n", run_count); run_count = 0; RAQM_TEST ("Final Runs:\n"); for (raqm_run_t *run = rq->runs; run != NULL; run = run->next) { SCRIPT_TO_STRING (run->script); RAQM_TEST ("run[%d]:\t start: %d\tlength: %d\tdirection: %s\tscript: %s\n", run_count++, run->pos, run->len, hb_direction_to_string (run->direction), buff); } RAQM_TEST ("\n"); #endif free (runs); return ok; }