From: Nikolay Sivov nsivov@codeweavers.com
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/dwrite/analyzer.c | 27 ++++++++++++++++----------- dlls/dwrite/tests/analyzer.c | 10 ++++++++++ 2 files changed, 26 insertions(+), 11 deletions(-)
diff --git a/dlls/dwrite/analyzer.c b/dlls/dwrite/analyzer.c index 85cbfab74d5..c2a4c6f22a3 100644 --- a/dlls/dwrite/analyzer.c +++ b/dlls/dwrite/analyzer.c @@ -654,17 +654,22 @@ static DWRITE_SCRIPT_ANALYSIS get_char_sa(UINT32 c)
sa.script = get_char_script(c); sa.shapes = DWRITE_SCRIPT_SHAPES_DEFAULT; - if ((c >= 0x0001 && c <= 0x001f) || - (c >= 0x007f && c <= 0x009f) || - (c == 0x00ad) /* SOFT HYPHEN */ || - (c >= 0x200b && c <= 0x200f) || - (c >= 0x2028 && c <= 0x202e) || - (c >= 0x2060 && c <= 0x2064) || - (c >= 0x2066 && c <= 0x206f) || - (c == 0xfeff) || - (c == 0xfff9) || - (c == 0xfffa) || - (c == 0xfffb)) + if ((c >= 0x0001 && c <= 0x001f) /* C0 controls */ + || (c >= 0x007f && c <= 0x009f) /* DELETE, C1 controls */ + || (c == 0x00ad) /* SOFT HYPHEN */ + || (c >= 0x200b && c <= 0x200f) /* ZWSP, ZWNJ, ZWJ, LRM, RLM */ + || (c >= 0x2028 && c <= 0x202e) /* Line/paragraph separators, LRE, RLE, PDF, LRO, RLO */ + || (c >= 0x2060 && c <= 0x2064) /* WJ, invisible operators */ + || (c >= 0x2066 && c <= 0x2069) /* LRI, RLI, FSI, PDI */ + || (c >= 0x206a && c <= 0x206f) /* Deprecated control characters */ + || (c == 0xfeff) /* ZWBNSP */ + || (c == 0xfff9) /* Interlinear annotation */ + || (c == 0xfffa) + || (c == 0xfffb) + || (c >= 0x1bca0 && c <= 0x1bca3) /* Shorthand format controls */ + || (c >= 0x1d173 && c <= 0x1d17a) /* Musical symbols: beams and slurs */ + || (c == 0xe0001) /* Language tag, deprecated */ + || (c >= 0xe0020 && c <= 0xe007f)) /* Tag components */ { sa.shapes = DWRITE_SCRIPT_SHAPES_NO_VISUAL; } diff --git a/dlls/dwrite/tests/analyzer.c b/dlls/dwrite/tests/analyzer.c index aac0fcc0e01..0363293197e 100644 --- a/dlls/dwrite/tests/analyzer.c +++ b/dlls/dwrite/tests/analyzer.c @@ -1026,6 +1026,16 @@ static struct sa_test sa_tests[] = { {0xd805,0xde80,0}, 1, { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT } } }, + { + /* Musical symbols, U+1D173 */ + {0xd834,0xdd73,0}, 1, + { { 0, 2, DWRITE_SCRIPT_SHAPES_NO_VISUAL } } + }, + { + /* Tags, U+E0020 */ + {0xdb40,0xdc20,0}, 1, + { { 0, 2, DWRITE_SCRIPT_SHAPES_NO_VISUAL } } + }, /* keep this as end test data marker */ { {0} } };
From: Nikolay Sivov nsivov@codeweavers.com
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/dwrite/analyzer.c | 121 ++++++---------- dlls/dwrite/bidi.c | 273 ++++++++++++++++++----------------- dlls/dwrite/dwrite_private.h | 11 +- 3 files changed, 199 insertions(+), 206 deletions(-)
diff --git a/dlls/dwrite/analyzer.c b/dlls/dwrite/analyzer.c index c2a4c6f22a3..9c7f636031c 100644 --- a/dlls/dwrite/analyzer.c +++ b/dlls/dwrite/analyzer.c @@ -30,6 +30,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(dwrite);
extern const unsigned short wine_linebreak_table[] DECLSPEC_HIDDEN; extern const unsigned short wine_scripts_table[] DECLSPEC_HIDDEN; +extern const unsigned short bidi_direction_table[] DECLSPEC_HIDDEN;
/* Number of characters needed for LOCALE_SNATIVEDIGITS */ #define NATIVE_DIGITS_LEN 11 @@ -1224,50 +1225,6 @@ static ULONG WINAPI dwritetextanalyzer_Release(IDWriteTextAnalyzer2 *iface) return 1; }
-/* This helper tries to get 'length' chars from a source, allocating a buffer only if source failed to provide enough - data after a first request. */ -static HRESULT get_text_source_ptr(IDWriteTextAnalysisSource *source, UINT32 position, UINT32 length, const WCHAR **text, WCHAR **buff) -{ - HRESULT hr; - UINT32 len; - - *buff = NULL; - *text = NULL; - len = 0; - hr = IDWriteTextAnalysisSource_GetTextAtPosition(source, position, text, &len); - if (FAILED(hr)) return hr; - - if (len < length) { - UINT32 read; - - *buff = calloc(length, sizeof(WCHAR)); - if (!*buff) - return E_OUTOFMEMORY; - if (*text) - memcpy(*buff, *text, len*sizeof(WCHAR)); - read = len; - - while (read < length && *text) { - *text = NULL; - len = 0; - hr = IDWriteTextAnalysisSource_GetTextAtPosition(source, position+read, text, &len); - if (FAILED(hr)) - { - free(*buff); - return hr; - } - if (!*text) - break; - memcpy(*buff + read, *text, min(len, length-read)*sizeof(WCHAR)); - read += len; - } - - *text = *buff; - } - - return hr; -} - static HRESULT WINAPI dwritetextanalyzer_AnalyzeScript(IDWriteTextAnalyzer2 *iface, IDWriteTextAnalysisSource* source, UINT32 position, UINT32 length, IDWriteTextAnalysisSink* sink) { @@ -1284,14 +1241,24 @@ static HRESULT WINAPI dwritetextanalyzer_AnalyzeScript(IDWriteTextAnalyzer2 *ifa return analyze_script(&context, sink); }
+static inline unsigned int get_bidi_char_length(const struct bidi_char *c) +{ + return c->ch > 0xffff ? 2 : 1; +} + +static inline UINT8 get_char_bidi_class(UINT32 ch) +{ + return get_table_entry(bidi_direction_table, ch); +} + static HRESULT WINAPI dwritetextanalyzer_AnalyzeBidi(IDWriteTextAnalyzer2 *iface, IDWriteTextAnalysisSource* source, UINT32 position, UINT32 length, IDWriteTextAnalysisSink* sink) { - UINT8 *levels = NULL, *explicit = NULL; - UINT8 baselevel, level, explicit_level; - UINT32 pos, i, seq_length; - WCHAR *buff = NULL; - const WCHAR *text; + struct text_source_context context; + UINT8 baselevel, resolved, explicit; + unsigned int i, chars_count = 0; + struct bidi_char *chars, *ptr; + UINT32 pos, seq_length; HRESULT hr;
TRACE("%p, %u, %u, %p.\n", source, position, length, sink); @@ -1299,49 +1266,57 @@ static HRESULT WINAPI dwritetextanalyzer_AnalyzeBidi(IDWriteTextAnalyzer2 *iface if (!length) return S_OK;
- hr = get_text_source_ptr(source, position, length, &text, &buff); - if (FAILED(hr)) - return hr; + if (!(chars = calloc(length, sizeof(*chars)))) + return E_OUTOFMEMORY;
- levels = calloc(length, sizeof(*levels)); - explicit = calloc(length, sizeof(*explicit)); + ptr = chars; + text_source_context_init(&context, source, position, length); + while (!text_source_get_next_u32_char(&context)) + { + ptr->ch = context.ch; + ptr->nominal_bidi_class = ptr->bidi_class = get_char_bidi_class(context.ch); + ptr++;
- if (!levels || !explicit) { - hr = E_OUTOFMEMORY; - goto done; + ++chars_count; }
+ /* Resolve levels using utf-32 codepoints, size differences will accounted for + when levels are reported with SetBidiLevel(). */ + baselevel = IDWriteTextAnalysisSource_GetParagraphReadingDirection(source); - hr = bidi_computelevels(text, length, baselevel, explicit, levels); + hr = bidi_computelevels(chars, chars_count, baselevel); if (FAILED(hr)) goto done;
- level = levels[0]; - explicit_level = explicit[0]; pos = position; - seq_length = 1; + resolved = chars->resolved; + explicit = chars->explicit; + seq_length = get_bidi_char_length(chars);
- for (i = 1; i < length; i++) { - if (levels[i] == level && explicit[i] == explicit_level) - seq_length++; - else { - hr = IDWriteTextAnalysisSink_SetBidiLevel(sink, pos, seq_length, explicit_level, level); + for (i = 1, ptr = chars + 1; i < chars_count; ++i, ++ptr) + { + if (ptr->resolved == resolved && ptr->explicit == explicit) + { + seq_length += get_bidi_char_length(ptr); + } + else + { + hr = IDWriteTextAnalysisSink_SetBidiLevel(sink, pos, seq_length, explicit, resolved); if (FAILED(hr)) goto done;
pos += seq_length; - seq_length = 1; - level = levels[i]; - explicit_level = explicit[i]; + seq_length = get_bidi_char_length(ptr); + resolved = ptr->resolved; + explicit = ptr->explicit; } } + /* one char length case or normal completion call */ - hr = IDWriteTextAnalysisSink_SetBidiLevel(sink, pos, seq_length, explicit_level, level); + hr = IDWriteTextAnalysisSink_SetBidiLevel(sink, pos, seq_length, explicit, resolved);
done: - free(explicit); - free(levels); - free(buff); + free(chars);
return hr; } diff --git a/dlls/dwrite/bidi.c b/dlls/dwrite/bidi.c index cc62509b557..3c47cdf76cd 100644 --- a/dlls/dwrite/bidi.c +++ b/dlls/dwrite/bidi.c @@ -136,28 +136,19 @@ static const char debug_type[][4] = "PDI", };
-static inline void bidi_dump_types(const char* header, const UINT8 *types, UINT32 start, UINT32 end) +static inline void bidi_dump_types(const char* header, const struct bidi_char *chars, UINT32 start, UINT32 end) { int i, len = 0; TRACE("%s:", header); for (i = start; i < end && len < 200; i++) { - TRACE(" %s", debug_type[types[i]]); - len += strlen(debug_type[types[i]])+1; + TRACE(" %s", debug_type[chars[i].bidi_class]); + len += strlen(debug_type[chars[i].bidi_class]) + 1; } if (i != end) TRACE("..."); TRACE("\n"); }
-/* Convert the libwine information to the direction enum */ -static void bidi_classify(const WCHAR *string, UINT8 *chartype, UINT32 count) -{ - UINT32 i; - - for (i = 0; i < count; ++i) - chartype[i] = get_table_entry( bidi_direction_table, string[i] ); -} - /* RESOLVE EXPLICIT */
static inline UINT8 get_greater_even_level(UINT8 level) @@ -181,14 +172,6 @@ static inline UINT8 get_embedding_direction(UINT8 level) Recursively resolves explicit embedding levels and overrides. Implements rules X1-X9, of the Unicode Bidirectional Algorithm.
- Input: Base embedding level and direction - Character count - - Output: Array of embedding levels - - In/Out: Array of direction classes - - Note: The function uses two simple counters to keep track of matching explicit codes and PDF. Use the default argument for the outermost call. The nesting counter counts the recursion @@ -211,13 +194,13 @@ typedef struct tagStackItem
#define valid_level(x) (x <= MAX_DEPTH && overflow_isolate_count == 0 && overflow_embedding_count == 0)
-static void bidi_resolve_explicit(UINT8 baselevel, UINT8 *classes, UINT8 *levels, UINT32 count) +static void bidi_resolve_explicit(struct bidi_char *chars, unsigned int count, UINT8 baselevel) { /* X1 */ int overflow_isolate_count = 0; int overflow_embedding_count = 0; int valid_isolate_count = 0; - UINT32 i; + unsigned int i;
StackItem stack[MAX_DEPTH+2]; int stack_top = MAX_DEPTH+1; @@ -226,15 +209,18 @@ static void bidi_resolve_explicit(UINT8 baselevel, UINT8 *classes, UINT8 *levels stack[stack_top].override = NI; stack[stack_top].isolate = FALSE;
- for (i = 0; i < count; i++) { + for (i = 0; i < count; ++i) + { + struct bidi_char *c = &chars[i]; UINT8 least_odd, least_even;
- switch (classes[i]) { + switch (c->bidi_class) + {
/* X2 */ case RLE: least_odd = get_greater_odd_level(stack[stack_top].level); - levels[i] = valid_level(least_odd) ? least_odd : stack[stack_top].level; + c->resolved = valid_level(least_odd) ? least_odd : stack[stack_top].level; if (valid_level(least_odd)) push_stack(least_odd, NI, FALSE); else if (overflow_isolate_count == 0) @@ -244,7 +230,7 @@ static void bidi_resolve_explicit(UINT8 baselevel, UINT8 *classes, UINT8 *levels /* X3 */ case LRE: least_even = get_greater_even_level(stack[stack_top].level); - levels[i] = valid_level(least_even) ? least_even : stack[stack_top].level; + c->resolved = valid_level(least_even) ? least_even : stack[stack_top].level; if (valid_level(least_even)) push_stack(least_even, NI, FALSE); else if (overflow_isolate_count == 0) @@ -254,7 +240,7 @@ static void bidi_resolve_explicit(UINT8 baselevel, UINT8 *classes, UINT8 *levels /* X4 */ case RLO: least_odd = get_greater_odd_level(stack[stack_top].level); - levels[i] = stack[stack_top].level; + c->resolved = stack[stack_top].level; if (valid_level(least_odd)) push_stack(least_odd, R, FALSE); else if (overflow_isolate_count == 0) @@ -264,7 +250,7 @@ static void bidi_resolve_explicit(UINT8 baselevel, UINT8 *classes, UINT8 *levels /* X5 */ case LRO: least_even = get_greater_even_level(stack[stack_top].level); - levels[i] = stack[stack_top].level; + c->resolved = stack[stack_top].level; if (valid_level(least_even)) push_stack(least_even, L, FALSE); else if (overflow_isolate_count == 0) @@ -274,7 +260,7 @@ static void bidi_resolve_explicit(UINT8 baselevel, UINT8 *classes, UINT8 *levels /* X5a */ case RLI: least_odd = get_greater_odd_level(stack[stack_top].level); - levels[i] = stack[stack_top].level; + c->resolved = stack[stack_top].level; if (valid_level(least_odd)) { valid_isolate_count++; @@ -287,7 +273,7 @@ static void bidi_resolve_explicit(UINT8 baselevel, UINT8 *classes, UINT8 *levels /* X5b */ case LRI: least_even = get_greater_even_level(stack[stack_top].level); - levels[i] = stack[stack_top].level; + c->resolved = stack[stack_top].level; if (valid_level(least_even)) { valid_isolate_count++; @@ -304,15 +290,17 @@ static void bidi_resolve_explicit(UINT8 baselevel, UINT8 *classes, UINT8 *levels int skipping = 0; int j;
- levels[i] = stack[stack_top].level; + c->resolved = stack[stack_top].level; for (j = i+1; j < count; j++) { - if (classes[j] == LRI || classes[j] == RLI || classes[j] == FSI) + const struct bidi_char *p = &chars[j]; + + if (p->bidi_class == LRI || p->bidi_class == RLI || p->bidi_class == FSI) { skipping++; continue; } - else if (classes[j] == PDI) + else if (p->bidi_class == PDI) { if (skipping) skipping --; @@ -323,12 +311,12 @@ static void bidi_resolve_explicit(UINT8 baselevel, UINT8 *classes, UINT8 *levels
if (skipping) continue;
- if (classes[j] == L) + if (p->bidi_class == L) { new_level = 0; break; } - else if (classes[j] == R || classes[j] == AL) + else if (p->bidi_class == R || p->bidi_class == AL) { new_level = 1; break; @@ -372,9 +360,9 @@ static void bidi_resolve_explicit(UINT8 baselevel, UINT8 *classes, UINT8 *levels case ET: case S: case WS: - levels[i] = stack[stack_top].level; + c->resolved = stack[stack_top].level; if (stack[stack_top].override != NI) - classes[i] = stack[stack_top].override; + c->resolved = stack[stack_top].override; break;
/* X6a */ @@ -388,12 +376,12 @@ static void bidi_resolve_explicit(UINT8 baselevel, UINT8 *classes, UINT8 *levels pop_stack(); valid_isolate_count--; } - levels[i] = stack[stack_top].level; + c->resolved = stack[stack_top].level; break;
/* X7 */ case PDF: - levels[i] = stack[stack_top].level; + c->resolved = stack[stack_top].level; if (overflow_isolate_count) {/* do nothing */} else if (overflow_embedding_count) overflow_embedding_count--; else if (!stack[stack_top].isolate && stack_top < (MAX_DEPTH+1)) @@ -402,29 +390,44 @@ static void bidi_resolve_explicit(UINT8 baselevel, UINT8 *classes, UINT8 *levels
/* X8 */ default: - levels[i] = baselevel; + c->resolved = baselevel; break; } + + c->explicit = c->resolved; } + /* X9: Based on 5.2 Retaining Explicit Formatting Characters */ - for (i = 0; i < count ; i++) - if (classes[i] == RLE || classes[i] == LRE || classes[i] == RLO || classes[i] == LRO || classes[i] == PDF) - classes[i] = BN; + for (i = 0; i < count; ++i) + { + switch (chars[i].bidi_class) + { + case RLE: + case LRE: + case RLO: + case LRO: + case PDF: + chars[i].bidi_class = BN; + break; + default: + ; + } + } }
-static inline int get_prev_valid_char_index(const UINT8 *classes, int index, int back_fence) +static inline int get_prev_valid_char_index(const struct bidi_char *chars, int index, int back_fence) { if (index == -1 || index == back_fence) return index; index--; - while (index > back_fence && classes[index] == BN) index--; + while (index > back_fence && chars[index].bidi_class == BN) index--; return index; }
-static inline int get_next_valid_char_index(const UINT8 *classes, int index, int front_fence) +static inline int get_next_valid_char_index(const struct bidi_char *chars, int index, int front_fence) { if (index == front_fence) return index; index++; - while (index < front_fence && classes[index] == BN) index++; + while (index < front_fence && chars[index].bidi_class == BN) index++; return index; }
@@ -841,34 +844,31 @@ static void bidi_resolve_neutrals(IsolatedRun *run) Recursively resolves implicit embedding levels. Implements rules I1 and I2 of the Unicode Bidirectional Algorithm.
- Input: Array of direction classes - Character count - Base level - - In/Out: Array of embedding levels - Note: levels may exceed 15 on output. Accepted subset of direction classes R, L, AN, EN ------------------------------------------------------------------------*/ -static void bidi_resolve_implicit(const UINT8 *classes, UINT8 *levels, int sos, int eos) +static void bidi_resolve_implicit(struct bidi_char *chars, unsigned int count) { - int i; + unsigned int i;
/* I1/2 */ - for (i = sos; i <= eos; i++) { - if (classes[i] == BN) + for (i = 0; i < count; ++i) + { + struct bidi_char *c = &chars[i]; + + if (c->bidi_class == BN) continue;
- ASSERT(classes[i] != ON); /* "No Neutrals allowed to survive here." */ - ASSERT(classes[i] <= EN); /* "Out of range." */ + ASSERT(c->bidi_class != ON); /* "No Neutrals allowed to survive here." */ + ASSERT(c->bidi_class <= EN); /* "Out of range." */
- if (odd(levels[i]) && (classes[i] == L || classes[i] == EN || classes[i] == AN)) - levels[i]++; - else if (!odd(levels[i]) && classes[i] == R) - levels[i]++; - else if (!odd(levels[i]) && (classes[i] == EN || classes[i] == AN)) - levels[i] += 2; + if (odd(c->resolved) && (c->bidi_class == L || c->bidi_class == EN || c->bidi_class == AN)) + c->resolved++; + else if (!odd(c->resolved) && c->bidi_class == R) + c->resolved++; + else if (!odd(c->resolved) && (c->bidi_class == EN || c->bidi_class == AN)) + c->resolved += 2; } }
@@ -892,31 +892,41 @@ static inline BOOL is_rule_L1_reset_class(UINT8 class) } }
-static void bidi_resolve_resolved(UINT8 baselevel, const UINT8 *classes, UINT8 *levels, int sos, int eos) +static void bidi_resolve_resolved(struct bidi_char *chars, unsigned int count, UINT8 baselevel) { - int i; + int i, sos = 0, eos = count - 1;
/* L1 */ - for (i = sos; i <= eos; i++) { - if (classes[i] == B || classes[i] == S) { - int j = i - 1; - while (i > sos && j >= sos && is_rule_L1_reset_class(classes[j])) - levels[j--] = baselevel; - levels[i] = baselevel; - } - else if (classes[i] == LRE || classes[i] == RLE || classes[i] == LRO || classes[i] == RLO || - classes[i] == PDF || classes[i] == BN) { - levels[i] = i ? levels[i - 1] : baselevel; + for (i = sos; i <= eos; i++) + { + switch (chars[i].nominal_bidi_class) + { + case B: + case S: + { + int j = i - 1; + while (i > sos && j >= sos && is_rule_L1_reset_class(chars[j].nominal_bidi_class)) + chars[j--].resolved = baselevel; + chars[i].resolved = baselevel; + } + break; + case LRE: case RLE: case LRO: case RLO: case PDF: case BN: + chars[i].resolved = i ? chars[i - 1].resolved : baselevel; + break; + default: + ; } - if (i == eos && is_rule_L1_reset_class(classes[i])) { + + if (i == eos && is_rule_L1_reset_class(chars[i].nominal_bidi_class)) + { int j = i; - while (j >= sos && is_rule_L1_reset_class(classes[j])) - levels[j--] = baselevel; + while (j >= sos && is_rule_L1_reset_class(chars[j].nominal_bidi_class)) + chars[j--].resolved = baselevel; } } }
-static HRESULT bidi_compute_isolating_runs_set(UINT8 baselevel, UINT8 *classes, UINT8 *levels, const WCHAR *string, UINT32 count, struct list *set) +static HRESULT bidi_compute_isolating_runs_set(struct bidi_char *chars, unsigned int count, UINT8 baselevel, struct list *set) { int run_start, run_end, i; int run_count = 0; @@ -930,23 +940,26 @@ static HRESULT bidi_compute_isolating_runs_set(UINT8 baselevel, UINT8 *classes,
/* Build Runs */ run_start = 0; - while (run_start < count) { - run_end = get_next_valid_char_index(classes, run_start, count); - while (run_end < count && levels[run_end] == levels[run_start]) - run_end = get_next_valid_char_index(classes, run_end, count); + while (run_start < count) + { + run_end = get_next_valid_char_index(chars, run_start, count); + while (run_end < count && chars[run_end].resolved == chars[run_start].resolved) + run_end = get_next_valid_char_index(chars, run_end, count); run_end--; runs[run_count].start = run_start; runs[run_count].end = run_end; - runs[run_count].e = levels[run_start]; - run_start = get_next_valid_char_index(classes, run_end, count); + runs[run_count].e = chars[run_start].resolved; + run_start = get_next_valid_char_index(chars, run_end, count); run_count++; }
/* Build Isolating Runs */ i = 0; - while (i < run_count) { + while (i < run_count) + { int k = i; - if (runs[k].start >= 0) { + if (runs[k].start >= 0) + { IsolatedRun *current_isolated; int type_fence, real_end; int j; @@ -961,42 +974,45 @@ static HRESULT bidi_compute_isolating_runs_set(UINT8 baselevel, UINT8 *classes, current_isolated->e = runs[k].e; current_isolated->length = (runs[k].end - runs[k].start)+1;
- for (j = 0; j < current_isolated->length; j++) { - current_isolated->item[j].class = &classes[runs[k].start+j]; - current_isolated->item[j].ch = string[runs[k].start+j]; + for (j = 0; j < current_isolated->length; ++j) + { + current_isolated->item[j].class = &chars[runs[k].start+j].bidi_class; + current_isolated->item[j].ch = chars[runs[k].start+j].ch; }
run_end = runs[k].end;
TRACE("{ [%i -- %i]",run_start, run_end);
- if (classes[run_end] == BN) - run_end = get_prev_valid_char_index(classes, run_end, runs[k].start); + if (chars[run_end].bidi_class == BN) + run_end = get_prev_valid_char_index(chars, run_end, runs[k].start);
- while (run_end < count && (classes[run_end] == RLI || classes[run_end] == LRI || classes[run_end] == FSI)) { + while (run_end < count && (chars[run_end].bidi_class == RLI || chars[run_end].bidi_class == LRI || chars[run_end].bidi_class == FSI)) + { j = k+1; search: - while (j < run_count && classes[runs[j].start] != PDI) j++; + while (j < run_count && chars[runs[j].start].bidi_class != PDI) j++; if (j < run_count && runs[i].e != runs[j].e) { j++; goto search; }
- if (j != run_count) { + if (j != run_count) + { int l = current_isolated->length; int m;
current_isolated->length += (runs[j].end - runs[j].start)+1; for (m = 0; l < current_isolated->length; l++, m++) { - current_isolated->item[l].class = &classes[runs[j].start+m]; - current_isolated->item[l].ch = string[runs[j].start+m]; + current_isolated->item[l].class = &chars[runs[j].start + m].bidi_class; + current_isolated->item[l].ch = chars[runs[j].start + m].ch; }
TRACE("[%i -- %i]", runs[j].start, runs[j].end);
run_end = runs[j].end; - if (classes[run_end] == BN) - run_end = get_prev_valid_char_index(classes, run_end, runs[i].start); + if (chars[run_end].bidi_class == BN) + run_end = get_prev_valid_char_index(chars, run_end, runs[i].start); runs[j].start = -1; k = j; } @@ -1006,32 +1022,34 @@ search: } }
- type_fence = get_prev_valid_char_index(classes, run_start, -1); + type_fence = get_prev_valid_char_index(chars, run_start, -1);
if (type_fence == -1) - current_isolated->sos = (baselevel > levels[run_start]) ? baselevel : levels[run_start]; + current_isolated->sos = max(baselevel, chars[run_start].resolved); else - current_isolated->sos = (levels[type_fence] > levels[run_start]) ? levels[type_fence] : levels[run_start]; + current_isolated->sos = max(chars[type_fence].resolved, chars[run_start].resolved);
current_isolated->sos = get_embedding_direction(current_isolated->sos);
if (run_end == count) current_isolated->eos = current_isolated->sos; - else { + else + { /* eos could be an BN */ - if (classes[run_end] == BN) { - real_end = get_prev_valid_char_index(classes, run_end, run_start-1); + if (chars[run_end].resolved == BN) + { + real_end = get_prev_valid_char_index(chars, run_end, run_start - 1); if (real_end < run_start) real_end = run_start; } else real_end = run_end;
- type_fence = get_next_valid_char_index(classes, run_end, count); + type_fence = get_next_valid_char_index(chars, run_end, count); if (type_fence == count) - current_isolated->eos = (baselevel > levels[real_end]) ? baselevel : levels[real_end]; + current_isolated->eos = max(baselevel, chars[real_end].resolved); else - current_isolated->eos = (levels[type_fence] > levels[real_end]) ? levels[type_fence] : levels[real_end]; + current_isolated->eos = max(chars[type_fence].resolved, chars[real_end].resolved);
current_isolated->eos = get_embedding_direction(current_isolated->eos); } @@ -1046,30 +1064,24 @@ search: return hr; }
-HRESULT bidi_computelevels(const WCHAR *string, UINT32 count, UINT8 baselevel, UINT8 *explicit, UINT8 *levels) +HRESULT bidi_computelevels(struct bidi_char *chars, unsigned int count, UINT8 baselevel) { IsolatedRun *iso_run, *next; struct list IsolatingRuns; - UINT8 *chartype; HRESULT hr;
- TRACE("%s, %u\n", debugstr_wn(string, count), count); - - if (!(chartype = malloc(count * sizeof(*chartype)))) - return E_OUTOFMEMORY; - - bidi_classify(string, chartype, count); - if (TRACE_ON(bidi)) bidi_dump_types("start ", chartype, 0, count); + if (TRACE_ON(bidi)) bidi_dump_types("start ", chars, 0, count);
- bidi_resolve_explicit(baselevel, chartype, levels, count); - memcpy(explicit, levels, count*sizeof(*explicit)); + bidi_resolve_explicit(chars, count, baselevel);
- if (TRACE_ON(bidi)) bidi_dump_types("after explicit", chartype, 0, count); + if (TRACE_ON(bidi)) bidi_dump_types("after explicit", chars, 0, count);
/* X10/BD13: Compute Isolating runs */ - hr = bidi_compute_isolating_runs_set(baselevel, chartype, levels, string, count, &IsolatingRuns); - if (FAILED(hr)) - goto done; + if (FAILED(hr = bidi_compute_isolating_runs_set(chars, count, baselevel, &IsolatingRuns))) + { + WARN("Failed to compute isolating runs set, hr %#lx.\n", hr); + return hr; + }
LIST_FOR_EACH_ENTRY_SAFE(iso_run, next, &IsolatingRuns, IsolatedRun, entry) { @@ -1085,13 +1097,10 @@ HRESULT bidi_computelevels(const WCHAR *string, UINT32 count, UINT8 baselevel, U free(iso_run); }
- if (TRACE_ON(bidi)) bidi_dump_types("before implicit", chartype, 0, count); - bidi_resolve_implicit(chartype, levels, 0, count-1); + if (TRACE_ON(bidi)) bidi_dump_types("before implicit", chars, 0, count); + bidi_resolve_implicit(chars, count);
- bidi_classify(string, chartype, count); - bidi_resolve_resolved(baselevel, chartype, levels, 0, count-1); + bidi_resolve_resolved(chars, count, baselevel);
-done: - free(chartype); - return hr; + return S_OK; } diff --git a/dlls/dwrite/dwrite_private.h b/dlls/dwrite/dwrite_private.h index f274c8acb0f..7e84bddd2ee 100644 --- a/dlls/dwrite/dwrite_private.h +++ b/dlls/dwrite/dwrite_private.h @@ -530,7 +530,16 @@ enum gasp_flags { extern unsigned int opentype_get_gasp_flags(const struct dwrite_fonttable *gasp, float emsize) DECLSPEC_HIDDEN;
/* BiDi helpers */ -extern HRESULT bidi_computelevels(const WCHAR*,UINT32,UINT8,UINT8*,UINT8*) DECLSPEC_HIDDEN; +struct bidi_char +{ + unsigned int ch; + UINT8 explicit; + UINT8 resolved; + UINT8 nominal_bidi_class; + UINT8 bidi_class; +}; + +extern HRESULT bidi_computelevels(struct bidi_char *chars, unsigned int count, UINT8 baselevel) DECLSPEC_HIDDEN;
struct dwrite_glyphbitmap {
From: Nikolay Sivov nsivov@codeweavers.com
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/dwrite/analyzer.c | 161 ++++++++++++++++++++++------------------- 1 file changed, 88 insertions(+), 73 deletions(-)
diff --git a/dlls/dwrite/analyzer.c b/dlls/dwrite/analyzer.c index 9c7f636031c..06028826112 100644 --- a/dlls/dwrite/analyzer.c +++ b/dlls/dwrite/analyzer.c @@ -732,8 +732,16 @@ static HRESULT analyze_script(struct text_source_context *context, IDWriteTextAn return IDWriteTextAnalysisSink_SetScriptAnalysis(sink, pos, length, &sa); }
-struct linebreaking_state { +struct break_index +{ + unsigned int index; + UINT8 length; +}; + +struct linebreaking_state +{ DWRITE_LINE_BREAKPOINT *breakpoints; + struct break_index *breaks; UINT32 count; };
@@ -804,19 +812,36 @@ static BOOL has_strong_condition(DWRITE_BREAK_CONDITION old_condition, DWRITE_BR static inline void set_break_condition(UINT32 pos, enum BreakConditionLocation location, DWRITE_BREAK_CONDITION condition, struct linebreaking_state *state) { - if (location == BreakConditionBefore) { - if (has_strong_condition(state->breakpoints[pos].breakConditionBefore, condition)) + unsigned int index = state->breaks[pos].index; + + if (location == BreakConditionBefore) + { + if (has_strong_condition(state->breakpoints[index].breakConditionBefore, condition)) return; - state->breakpoints[pos].breakConditionBefore = condition; - if (pos > 0) - state->breakpoints[pos-1].breakConditionAfter = condition; + state->breakpoints[index].breakConditionBefore = condition; + if (pos) + { + --pos; + + index = state->breaks[pos].index; + if (state->breaks[pos].length > 1) index++; + + state->breakpoints[index].breakConditionAfter = condition; + } } - else { - if (has_strong_condition(state->breakpoints[pos].breakConditionAfter, condition)) + else + { + if (state->breaks[pos].length > 1) index++; + + if (has_strong_condition(state->breakpoints[index].breakConditionAfter, condition)) return; - state->breakpoints[pos].breakConditionAfter = condition; + state->breakpoints[index].breakConditionAfter = condition; + if (pos + 1 < state->count) - state->breakpoints[pos+1].breakConditionBefore = condition; + { + index = state->breaks[pos + 1].index; + state->breakpoints[index].breakConditionBefore = condition; + } } }
@@ -826,43 +851,72 @@ BOOL lb_is_newline_char(WCHAR ch) return c == b_LF || c == b_NL || c == b_CR || c == b_BK; }
-static HRESULT analyze_linebreaks(const WCHAR *text, UINT32 count, DWRITE_LINE_BREAKPOINT *breakpoints) +static HRESULT analyze_linebreaks(IDWriteTextAnalysisSource *source, UINT32 position, + UINT32 length, DWRITE_LINE_BREAKPOINT *breakpoints) { + struct text_source_context context; struct linebreaking_state state; + struct break_index *breaks; + unsigned int count, index; short *break_class; - int i, j; + int i = 0, j; + HRESULT hr;
- if (!(break_class = calloc(count, sizeof(*break_class)))) - return E_OUTOFMEMORY; + if (FAILED(hr = text_source_context_init(&context, source, position, length))) return hr;
- state.breakpoints = breakpoints; - state.count = count; + if (!(breaks = calloc(length, sizeof(*breaks)))) + return E_OUTOFMEMORY;
- for (i = 0; i < count; i++) + if (!(break_class = calloc(length, sizeof(*break_class)))) { - break_class[i] = get_table_entry(wine_linebreak_table, text[i]); + free(breaks); + return E_OUTOFMEMORY; + }
- breakpoints[i].breakConditionBefore = DWRITE_BREAK_CONDITION_NEUTRAL; - breakpoints[i].breakConditionAfter = DWRITE_BREAK_CONDITION_NEUTRAL; - breakpoints[i].isWhitespace = !!iswspace(text[i]); - breakpoints[i].isSoftHyphen = text[i] == 0x00ad /* Unicode Soft Hyphen */; - breakpoints[i].padding = 0; + count = index = 0; + while (!text_source_get_next_u32_char(&context)) + { + break_class[count] = get_table_entry(wine_linebreak_table, context.ch); + breaks[count].length = text_source_get_char_length(&context); + breaks[count].index = index;
/* LB1 - resolve some classes. TODO: use external algorithms for these classes. */ - switch (break_class[i]) + switch (break_class[count]) { case b_AI: case b_SA: case b_SG: case b_XX: - break_class[i] = b_AL; + break_class[count] = b_AL; break; case b_CJ: - break_class[i] = b_NS; + break_class[count] = b_NS; break; } + + breakpoints[index].breakConditionBefore = DWRITE_BREAK_CONDITION_NEUTRAL; + breakpoints[index].breakConditionAfter = DWRITE_BREAK_CONDITION_NEUTRAL; + breakpoints[index].isWhitespace = context.ch < 0xffff ? !!iswspace(context.ch) : 0; + breakpoints[index].isSoftHyphen = context.ch == 0x00ad /* Unicode Soft Hyphen */; + breakpoints[index].padding = 0; + ++index; + + if (breaks[count].length > 1) + { + breakpoints[index] = breakpoints[index - 1]; + /* Never break in surrogate pairs. */ + breakpoints[index - 1].breakConditionAfter = DWRITE_BREAK_CONDITION_MAY_NOT_BREAK; + breakpoints[index].breakConditionBefore = DWRITE_BREAK_CONDITION_MAY_NOT_BREAK; + ++index; + } + + ++count; }
+ state.breakpoints = breakpoints; + state.breaks = breaks; + state.count = count; + /* LB2 - never break at the start */ set_break_condition(0, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state); /* LB3 - always break at the end. */ @@ -1193,6 +1247,8 @@ static HRESULT analyze_linebreaks(const WCHAR *text, UINT32 count, DWRITE_LINE_B }
free(break_class); + free(breaks); + return S_OK; }
@@ -1332,64 +1388,23 @@ static HRESULT WINAPI dwritetextanalyzer_AnalyzeNumberSubstitution(IDWriteTextAn }
static HRESULT WINAPI dwritetextanalyzer_AnalyzeLineBreakpoints(IDWriteTextAnalyzer2 *iface, - IDWriteTextAnalysisSource* source, UINT32 position, UINT32 length, IDWriteTextAnalysisSink* sink) + IDWriteTextAnalysisSource *source, UINT32 position, UINT32 length, IDWriteTextAnalysisSink *sink) { - DWRITE_LINE_BREAKPOINT *breakpoints = NULL; - WCHAR *buff = NULL; - const WCHAR *text; + DWRITE_LINE_BREAKPOINT *breakpoints; HRESULT hr; - UINT32 len;
TRACE("%p, %u, %u, %p.\n", source, position, length, sink);
- if (length == 0) + if (!length) return S_OK;
- /* get some, check for length */ - text = NULL; - len = 0; - hr = IDWriteTextAnalysisSource_GetTextAtPosition(source, position, &text, &len); - if (FAILED(hr)) return hr; - - if (len < length) { - UINT32 read; - - if (!(buff = calloc(length, sizeof(*buff)))) - return E_OUTOFMEMORY; - if (text) - memcpy(buff, text, len*sizeof(WCHAR)); - read = len; - - while (read < length && text) { - text = NULL; - len = 0; - hr = IDWriteTextAnalysisSource_GetTextAtPosition(source, position+read, &text, &len); - if (FAILED(hr)) - goto done; - if (!text) - break; - memcpy(&buff[read], text, min(len, length-read)*sizeof(WCHAR)); - read += len; - } - - text = buff; - } - if (!(breakpoints = calloc(length, sizeof(*breakpoints)))) - { - hr = E_OUTOFMEMORY; - goto done; - } - - hr = analyze_linebreaks(text, length, breakpoints); - if (FAILED(hr)) - goto done; + return E_OUTOFMEMORY;
- hr = IDWriteTextAnalysisSink_SetLineBreakpoints(sink, position, length, breakpoints); + if (SUCCEEDED(hr = analyze_linebreaks(source, position, length, breakpoints))) + hr = IDWriteTextAnalysisSink_SetLineBreakpoints(sink, position, length, breakpoints);
-done: free(breakpoints); - free(buff);
return hr; }
Marking draft for now, needs to be resolved after !786 is in.