dwrite: Improve line breaking logic.

Signed-off-by: Nikolay Sivov <nsivov@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Nikolay Sivov 2017-01-31 14:43:11 +03:00 committed by Alexandre Julliard
parent 76c31950fe
commit b280c3bf2e
1 changed files with 67 additions and 36 deletions

View File

@ -363,20 +363,31 @@ enum linebreaking_classes {
b_ZWJ, b_ZWJ,
}; };
static BOOL has_strong_condition(DWRITE_BREAK_CONDITION old_condition, DWRITE_BREAK_CONDITION new_condition)
{
if (old_condition == DWRITE_BREAK_CONDITION_MAY_NOT_BREAK || old_condition == DWRITE_BREAK_CONDITION_MUST_BREAK)
return TRUE;
if (old_condition == DWRITE_BREAK_CONDITION_CAN_BREAK && new_condition != DWRITE_BREAK_CONDITION_MUST_BREAK)
return TRUE;
return FALSE;
}
/* "Can break" is a weak condition, stronger "may not break" and "must break" override it. Initially all conditions are /* "Can break" is a weak condition, stronger "may not break" and "must break" override it. Initially all conditions are
set to "can break" and could only be changed once. */ set to "can break" and could only be changed once. */
static inline void set_break_condition(UINT32 pos, enum BreakConditionLocation location, DWRITE_BREAK_CONDITION condition, static inline void set_break_condition(UINT32 pos, enum BreakConditionLocation location, DWRITE_BREAK_CONDITION condition,
struct linebreaking_state *state) struct linebreaking_state *state)
{ {
if (location == BreakConditionBefore) { if (location == BreakConditionBefore) {
if (state->breakpoints[pos].breakConditionBefore != DWRITE_BREAK_CONDITION_CAN_BREAK) if (has_strong_condition(state->breakpoints[pos].breakConditionBefore, condition))
return; return;
state->breakpoints[pos].breakConditionBefore = condition; state->breakpoints[pos].breakConditionBefore = condition;
if (pos > 0) if (pos > 0)
state->breakpoints[pos-1].breakConditionAfter = condition; state->breakpoints[pos-1].breakConditionAfter = condition;
} }
else { else {
if (state->breakpoints[pos].breakConditionAfter != DWRITE_BREAK_CONDITION_CAN_BREAK) if (has_strong_condition(state->breakpoints[pos].breakConditionAfter, condition))
return; return;
state->breakpoints[pos].breakConditionAfter = condition; state->breakpoints[pos].breakConditionAfter = condition;
if (pos + 1 < state->count) if (pos + 1 < state->count)
@ -403,14 +414,12 @@ static HRESULT analyze_linebreaks(const WCHAR *text, UINT32 count, DWRITE_LINE_B
state.breakpoints = breakpoints; state.breakpoints = breakpoints;
state.count = count; state.count = count;
/* LB31 - allow breaks everywhere. It will be overridden if needed as
other rules dictate. */
for (i = 0; i < count; i++) for (i = 0; i < count; i++)
{ {
break_class[i] = get_table_entry(wine_linebreak_table, text[i]); break_class[i] = get_table_entry(wine_linebreak_table, text[i]);
breakpoints[i].breakConditionBefore = DWRITE_BREAK_CONDITION_CAN_BREAK; breakpoints[i].breakConditionBefore = DWRITE_BREAK_CONDITION_NEUTRAL;
breakpoints[i].breakConditionAfter = DWRITE_BREAK_CONDITION_CAN_BREAK; breakpoints[i].breakConditionAfter = DWRITE_BREAK_CONDITION_NEUTRAL;
breakpoints[i].isWhitespace = !!isspaceW(text[i]); breakpoints[i].isWhitespace = !!isspaceW(text[i]);
breakpoints[i].isSoftHyphen = text[i] == 0x00ad /* Unicode Soft Hyphen */; breakpoints[i].isSoftHyphen = text[i] == 0x00ad /* Unicode Soft Hyphen */;
breakpoints[i].padding = 0; breakpoints[i].padding = 0;
@ -432,8 +441,10 @@ static HRESULT analyze_linebreaks(const WCHAR *text, UINT32 count, DWRITE_LINE_B
/* LB2 - never break at the start */ /* LB2 - never break at the start */
set_break_condition(0, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state); set_break_condition(0, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
/* LB3 - always break at the end. This one is ignored. */ /* LB3 - always break at the end. */
set_break_condition(count - 1, BreakConditionAfter, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
/* LB4 - LB6 - mandatory breaks. */
for (i = 0; i < count; i++) for (i = 0; i < count; i++)
{ {
switch (break_class[i]) switch (break_class[i])
@ -455,16 +466,27 @@ static HRESULT analyze_linebreaks(const WCHAR *text, UINT32 count, DWRITE_LINE_B
/* LB6 - do not break before hard breaks */ /* LB6 - do not break before hard breaks */
set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state); set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
break; break;
}
}
/* LB7 - LB8 - explicit breaks and non-breaks */
for (i = 0; i < count; i++)
{
switch (break_class[i])
{
/* LB7 - do not break before spaces */ /* LB7 - do not break before spaces */
case b_SP: case b_SP:
set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state); set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
break; break;
case b_ZW: case b_ZW:
set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state); set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
/* LB8 - break before character after zero-width space, skip spaces in-between */
while (i < count-1 && break_class[i+1] == b_SP) /* LB8 - break before character after zero-width space, skip spaces in-between */
i++; j = i;
set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_CAN_BREAK, &state); while (j < count-1 && break_class[j+1] == b_SP)
j++;
if (j < count-1 && break_class[j+1] != b_ZW)
set_break_condition(j, BreakConditionAfter, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
break; break;
/* LB8a - do not break between ZWJ and an ideograph, emoji base or emoji modifier */ /* LB8a - do not break between ZWJ and an ideograph, emoji base or emoji modifier */
case b_ZWJ: case b_ZWJ:
@ -474,7 +496,7 @@ static HRESULT analyze_linebreaks(const WCHAR *text, UINT32 count, DWRITE_LINE_B
} }
} }
/* LB9 - LB10 */ /* LB9 - LB10 - combining marks */
for (i = 0; i < count; i++) for (i = 0; i < count; i++)
{ {
if (break_class[i] == b_CM || break_class[i] == b_ZWJ) if (break_class[i] == b_CM || break_class[i] == b_ZWJ)
@ -492,7 +514,10 @@ static HRESULT analyze_linebreaks(const WCHAR *text, UINT32 count, DWRITE_LINE_B
break_class[i] = b_AL; break_class[i] = b_AL;
break; break;
default: default:
{
break_class[i] = break_class[i-1]; break_class[i] = break_class[i-1];
set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
}
} }
} }
else break_class[i] = b_AL; else break_class[i] = b_AL;
@ -526,22 +551,20 @@ static HRESULT analyze_linebreaks(const WCHAR *text, UINT32 count, DWRITE_LINE_B
case b_SY: case b_SY:
set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state); set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
break; break;
/* LB14 */ /* LB14 - do not break after OP, even after spaces */
case b_OP: case b_OP:
set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state); j = i;
while (i < count-1 && break_class[i+1] == b_SP) { while (j < count-1 && break_class[j+1] == b_SP)
set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
i++;
}
break;
/* LB15 */
case b_QU:
j = i+1;
while (j < count-1 && break_class[j] == b_SP)
j++; j++;
if (break_class[j] == b_OP) set_break_condition(j, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
for (; j > i; j--) break;
set_break_condition(j, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state); /* LB15 - do not break within QU-OP, even with intervening spaces */
case b_QU:
j = i;
while (j < count-1 && break_class[j+1] == b_SP)
j++;
if (j < count - 1 && break_class[j+1] == b_OP)
set_break_condition(j, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
break; break;
/* LB16 */ /* LB16 */
case b_NS: case b_NS:
@ -552,14 +575,13 @@ static HRESULT analyze_linebreaks(const WCHAR *text, UINT32 count, DWRITE_LINE_B
for (j++; j <= i; j++) for (j++; j <= i; j++)
set_break_condition(j, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state); set_break_condition(j, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
break; break;
/* LB17 */ /* LB17 - do not break within B2, even with intervening spaces */
case b_B2: case b_B2:
j = i+1; j = i;
while (j < count && break_class[j] == b_SP) while (j < count && break_class[j+1] == b_SP)
j++; j++;
if (break_class[j] == b_B2) if (j < count - 1 && break_class[j+1] == b_B2)
for (; j > i; j--) set_break_condition(j, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
set_break_condition(j, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
break; break;
} }
} }
@ -580,7 +602,8 @@ static HRESULT analyze_linebreaks(const WCHAR *text, UINT32 count, DWRITE_LINE_B
/* LB20 */ /* LB20 */
case b_CB: case b_CB:
set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_CAN_BREAK, &state); set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_CAN_BREAK, &state); if (i < count - 1 && break_class[i+1] != b_QU)
set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
break; break;
/* LB21 */ /* LB21 */
case b_BA: case b_BA:
@ -589,7 +612,8 @@ static HRESULT analyze_linebreaks(const WCHAR *text, UINT32 count, DWRITE_LINE_B
set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state); set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
break; break;
case b_BB: case b_BB:
set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state); if (i < count - 1 && break_class[i+1] != b_CB)
set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
break; break;
/* LB21a, LB21b */ /* LB21a, LB21b */
case b_HL: case b_HL:
@ -701,7 +725,7 @@ static HRESULT analyze_linebreaks(const WCHAR *text, UINT32 count, DWRITE_LINE_B
if (break_class[i+1] == b_IN || break_class[i+1] == b_PO) if (break_class[i+1] == b_IN || break_class[i+1] == b_PO)
set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state); set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
} }
if (break_class[i] == b_PO) if (break_class[i] == b_PR)
{ {
switch (break_class[i+1]) switch (break_class[i+1])
{ {
@ -731,7 +755,7 @@ static HRESULT analyze_linebreaks(const WCHAR *text, UINT32 count, DWRITE_LINE_B
break_class[i+1] == b_OP) break_class[i+1] == b_OP)
set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state); set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
if (break_class[i] == b_CP && if (break_class[i] == b_CP &&
(break_class[i+1] == b_AL || break_class[i] == b_HL || break_class[i] == b_NU)) (break_class[i+1] == b_AL || break_class[i+1] == b_HL || break_class[i+1] == b_NU))
set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state); set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
/* LB30a - break between two RIs if and only if there are an even number of RIs preceding position of the break */ /* LB30a - break between two RIs if and only if there are an even number of RIs preceding position of the break */
@ -752,6 +776,13 @@ static HRESULT analyze_linebreaks(const WCHAR *text, UINT32 count, DWRITE_LINE_B
} }
} }
/* LB31 - allow breaks everywhere else. */
for (i = 0; i < count; i++)
{
set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
}
heap_free(break_class); heap_free(break_class);
return S_OK; return S_OK;
} }