diff options
author | Oleg Oshmyan <chortos@inbox.lv> | 2021-03-08 02:32:12 +0200 |
---|---|---|
committer | Oleg Oshmyan <chortos@inbox.lv> | 2022-02-14 20:43:08 +0200 |
commit | cc54eea644db29bc64bdaa9305576d4d18cae9ed (patch) | |
tree | 23c7b86f46b2687079e8c2884a6ab9b7ffa76327 | |
parent | 03cbb01cdf953bc4709035019e31e62dce238a4f (diff) | |
download | libass-cc54eea644db29bc64bdaa9305576d4d18cae9ed.tar.bz2 libass-cc54eea644db29bc64bdaa9305576d4d18cae9ed.tar.xz |
Bidi: remember resolved base direction
There used to be a FIXME comment about this,
but it was removed in commit 27cc03363cfbddb9877cd547327d56405653add7.
Reordering works fine without this *most of the time*, but this is still
a bug. It affects one thing only: trailing whitespace in each line/run.
Of course, we remove trailing U+0020 SPACE characters from each line
regardless of bidi, but Unicode bidi recognizes other whitespace,
and we can now also have trailing U+0020 within in-line runs.
With right-to-left base direction, all trailing whitespace within
each line (after line wrapping) or run are to be placed at the leftmost
end in visual order (even if that whitespace is nominally part
of a left-to-right embed). Conversely, with our current code that
always tells FriBidi the base direction is left-to-right here,
trailing whitespace is always placed at the rightmost end of each line,
even if it is inside right-to-left text (which makes it appear
as *leading* whitespace to the reader).
We treat explicit line breaks \N as paragraph separators,
where each paragraph can have its own base direction. Therefore,
we must remember each paragraph's resolved direction separately.
-rw-r--r-- | libass/ass_shaper.c | 31 |
1 files changed, 24 insertions, 7 deletions
diff --git a/libass/ass_shaper.c b/libass/ass_shaper.c index 3db939e..6cb2ebf 100644 --- a/libass/ass_shaper.c +++ b/libass/ass_shaper.c @@ -49,11 +49,12 @@ struct ass_shaper { ASS_ShapingLevel shaping_level; // FriBidi log2vis - int n_glyphs; + int n_glyphs, n_pars; FriBidiChar *event_text; FriBidiCharType *ctypes; FriBidiLevel *emblevels; FriBidiStrIndex *cmap; + FriBidiParType *pbase_dir; FriBidiParType base_direction; // OpenType features @@ -99,7 +100,7 @@ void ass_shaper_info(ASS_Library *lib) * \brief grow arrays, if needed * \param new_size requested size */ -static bool check_allocations(ASS_Shaper *shaper, size_t new_size) +static bool check_allocations(ASS_Shaper *shaper, size_t new_size, size_t n_pars) { if (new_size > shaper->n_glyphs) { if (!ASS_REALLOC_ARRAY(shaper->event_text, new_size) || @@ -112,6 +113,11 @@ static bool check_allocations(ASS_Shaper *shaper, size_t new_size) return false; shaper->n_glyphs = new_size; } + if (shaper->whole_text_layout && n_pars > shaper->n_pars) { + if (!ASS_REALLOC_ARRAY(shaper->pbase_dir, n_pars)) + return false; + shaper->n_pars = n_pars; + } return true; } @@ -129,6 +135,7 @@ void ass_shaper_free(ASS_Shaper *shaper) #endif free(shaper->emblevels); free(shaper->cmap); + free(shaper->pbase_dir); free(shaper); } @@ -965,10 +972,15 @@ void ass_shaper_set_whole_text_layout(ASS_Shaper *shaper, bool enable) bool ass_shaper_shape(ASS_Shaper *shaper, TextInfo *text_info) { int i, ret, last_break; - FriBidiParType dir; + FriBidiParType dir, *pdir; GlyphInfo *glyphs = text_info->glyphs; - if (!check_allocations(shaper, text_info->length)) + int n_pars = 1; + for (i = 0; i < text_info->length - 1; i++) + if (glyphs[i].symbol == '\n') + n_pars++; + + if (!check_allocations(shaper, text_info->length, n_pars)) return false; for (i = 0; i < text_info->length; i++) @@ -986,6 +998,7 @@ bool ass_shaper_shape(ASS_Shaper *shaper, TextInfo *text_info) // Get bidi embedding levels last_break = 0; + pdir = shaper->pbase_dir; for (i = 0; i < text_info->length; i++) { // embedding levels should be calculated paragraph by paragraph if (glyphs[i].symbol == '\n' || i == text_info->length - 1 || @@ -1006,6 +1019,8 @@ bool ass_shaper_shape(ASS_Shaper *shaper, TextInfo *text_info) if (ret == 0) return false; last_break = i + 1; + if (shaper->whole_text_layout) + *pdir++ = dir; } } @@ -1079,21 +1094,23 @@ FriBidiStrIndex *ass_shaper_reorder(ASS_Shaper *shaper, TextInfo *text_info) // Create reorder map line-by-line or run-by-run int last_break = 0; + FriBidiParType *pdir = shaper->whole_text_layout ? + shaper->pbase_dir : &shaper->base_direction; GlyphInfo *glyphs = text_info->glyphs; for (i = 0; i < text_info->length; i++) { if (i == text_info->length - 1 || glyphs[i + 1].linebreak || (!shaper->whole_text_layout && (glyphs[i + 1].starts_new_run || glyphs[i].hspacing))) { - FriBidiParType dir = FRIBIDI_PAR_ON; - ret = fribidi_reorder_line(0, - shaper->ctypes, i - last_break + 1, last_break, dir, + shaper->ctypes, i - last_break + 1, last_break, *pdir, shaper->emblevels, NULL, shaper->cmap); if (ret == 0) return NULL; last_break = i + 1; + if (shaper->whole_text_layout) + pdir++; } } |