usp10: Implement ScriptBreak using the Unicode Line Breaking Algorithm.
This commit is contained in:
parent
37dba06ed3
commit
95166855bb
|
@ -4,7 +4,9 @@ IMPORTS = gdi32
|
|||
|
||||
C_SRCS = \
|
||||
bidi.c \
|
||||
breaking.c \
|
||||
indic.c \
|
||||
linebreak.c \
|
||||
mirror.c \
|
||||
shape.c \
|
||||
shaping.c \
|
||||
|
|
|
@ -0,0 +1,410 @@
|
|||
/*
|
||||
* Implementation of line breaking algorighm for the Uniscribe Script Processor
|
||||
*
|
||||
* Copyright 2011 CodeWeavers, Aric Stewart
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*
|
||||
*/
|
||||
#include "config.h"
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "windef.h"
|
||||
#include "winbase.h"
|
||||
#include "winuser.h"
|
||||
#include "wingdi.h"
|
||||
#include "winnls.h"
|
||||
#include "usp10.h"
|
||||
#include "winternl.h"
|
||||
|
||||
#include "wine/debug.h"
|
||||
#include "usp10_internal.h"
|
||||
|
||||
WINE_DEFAULT_DEBUG_CHANNEL(uniscribe);
|
||||
|
||||
extern const unsigned short wine_linebreak_table[];
|
||||
|
||||
enum breaking_types { b_BK=1, b_CR, b_LF, b_CM, b_SG, b_GL, b_CB, b_SP, b_ZW, b_NL, b_WJ, b_JL, b_JV, b_JT, b_H2, b_H3, b_XX, b_OP, b_CL, b_CP, b_QU, b_NS, b_EX, b_SY, b_IS, b_PR, b_PO, b_NU, b_AL, b_ID, b_IN, b_HY, b_BB, b_BA, b_SA, b_AI, b_B2};
|
||||
|
||||
enum breaking_class {b_r=1, b_s, b_x};
|
||||
|
||||
static void debug_output_breaks(const short* breaks, int count)
|
||||
{
|
||||
if (TRACE_ON(uniscribe))
|
||||
{
|
||||
int i;
|
||||
TRACE("[");
|
||||
for (i = 0; i < count && i < 200; i++)
|
||||
{
|
||||
switch (breaks[i])
|
||||
{
|
||||
case b_x: TRACE("x"); break;
|
||||
case b_r: TRACE("!"); break;
|
||||
case b_s: TRACE("+"); break;
|
||||
default: TRACE("*");
|
||||
}
|
||||
}
|
||||
if (i == 200)
|
||||
TRACE("...");
|
||||
TRACE("]\n");
|
||||
}
|
||||
}
|
||||
|
||||
static inline void else_break(short* before, short class)
|
||||
{
|
||||
if (*before == 0) *before = class;
|
||||
}
|
||||
|
||||
void BREAK_line(const WCHAR *chars, int count, const SCRIPT_ANALYSIS *sa, SCRIPT_LOGATTR *la)
|
||||
{
|
||||
int i,j;
|
||||
short *break_class;
|
||||
short *break_before;
|
||||
|
||||
TRACE("In %s\n",debugstr_wn(chars,count));
|
||||
|
||||
break_class = HeapAlloc(GetProcessHeap(),0, count * sizeof(short));
|
||||
break_before = HeapAlloc(GetProcessHeap(),0, count * sizeof(short));
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
break_class[i] = wine_linebreak_table[wine_linebreak_table[chars[i] >> 8] + (chars[i] & 0xff)];
|
||||
break_before[i] = 0;
|
||||
|
||||
memset(&la[i],0,sizeof(SCRIPT_LOGATTR));
|
||||
|
||||
la[i].fCharStop = TRUE;
|
||||
switch (break_class[i])
|
||||
{
|
||||
case b_BK:
|
||||
case b_ZW:
|
||||
case b_SP:
|
||||
la[i].fWhiteSpace = TRUE;
|
||||
break;
|
||||
case b_CM:
|
||||
la[i].fCharStop = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/* LB1 */
|
||||
/* TODO: Have outside algorithims for these scripts */
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
switch(break_class[i])
|
||||
{
|
||||
case b_AI:
|
||||
case b_SA:
|
||||
case b_SG:
|
||||
case b_XX:
|
||||
break_class[i] = b_AL;
|
||||
}
|
||||
}
|
||||
|
||||
/* LB2 - LB3 */
|
||||
break_before[0] = b_x;
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
switch(break_class[i])
|
||||
{
|
||||
/* LB4 - LB6 */
|
||||
case b_CR:
|
||||
if (i < count-1 && break_class[i+1] == b_LF)
|
||||
{
|
||||
else_break(&break_before[i],b_x);
|
||||
else_break(&break_before[i+1],b_x);
|
||||
break;
|
||||
}
|
||||
case b_LF:
|
||||
case b_NL:
|
||||
case b_BK:
|
||||
if (i < count-1) else_break(&break_before[i+1],b_r);
|
||||
else_break(&break_before[i],b_x);
|
||||
break;
|
||||
/* LB7 */
|
||||
case b_SP:
|
||||
else_break(&break_before[i],b_x);
|
||||
break;
|
||||
case b_ZW:
|
||||
else_break(&break_before[i],b_x);
|
||||
/* LB8 */
|
||||
while (i < count-1 && break_class[i+1] == b_SP)
|
||||
i++;
|
||||
else_break(&break_before[i],b_s);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
debug_output_breaks(break_before,count);
|
||||
|
||||
/* LB9 - LB10 */
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
if (break_class[i] == b_CM)
|
||||
{
|
||||
if (i > 0)
|
||||
{
|
||||
switch (break_class[i-1])
|
||||
{
|
||||
case b_SP:
|
||||
case b_BK:
|
||||
case b_CR:
|
||||
case b_LF:
|
||||
case b_NL:
|
||||
case b_ZW:
|
||||
break_class[i] = b_AL;
|
||||
break;
|
||||
default:
|
||||
break_class[i] = break_class[i-1];
|
||||
}
|
||||
}
|
||||
else break_class[i] = b_AL;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
switch(break_class[i])
|
||||
{
|
||||
/* LB11 */
|
||||
case b_WJ:
|
||||
else_break(&break_before[i],b_x);
|
||||
if (i < count-1)
|
||||
else_break(&break_before[i+1],b_x);
|
||||
break;
|
||||
/* LB12 */
|
||||
case b_GL:
|
||||
if (i < count-1)
|
||||
else_break(&break_before[i+1],b_x);
|
||||
/* LB12a */
|
||||
if (i > 0)
|
||||
{
|
||||
if (break_class[i-1] != b_SP &&
|
||||
break_class[i-1] != b_BA &&
|
||||
break_class[i-1] != b_HY)
|
||||
else_break(&break_before[i],b_x);
|
||||
}
|
||||
break;
|
||||
/* LB13 */
|
||||
case b_CL:
|
||||
case b_CP:
|
||||
case b_EX:
|
||||
case b_IS:
|
||||
case b_SY:
|
||||
else_break(&break_before[i],b_x);
|
||||
break;
|
||||
/* LB14 */
|
||||
case b_OP:
|
||||
while (i < count-1 && break_class[i+1] == b_SP)
|
||||
{
|
||||
else_break(&break_before[i+1],b_x);
|
||||
i++;
|
||||
}
|
||||
else_break(&break_before[i+1],b_x);
|
||||
break;
|
||||
/* LB15 */
|
||||
case b_QU:
|
||||
j = i+1;
|
||||
while (j < count-1 && break_class[j] == b_SP)
|
||||
j++;
|
||||
if (break_class[j] == b_OP)
|
||||
{
|
||||
for (; j > i; j--)
|
||||
else_break(&break_before[j],b_x);
|
||||
}
|
||||
break;
|
||||
/* LB16 */
|
||||
case b_NS:
|
||||
j = i-1;
|
||||
while(j > 0 && break_class[j] == b_SP)
|
||||
j--;
|
||||
if (break_class[j] == b_CL || break_class[j] == b_CP)
|
||||
{
|
||||
for (j++; j <= i; j++)
|
||||
else_break(&break_before[j],b_x);
|
||||
}
|
||||
break;
|
||||
/* LB17 */
|
||||
case b_B2:
|
||||
j = i+1;
|
||||
while (j < count && break_class[j] == b_SP)
|
||||
j++;
|
||||
if (break_class[j] == b_B2)
|
||||
{
|
||||
for (; j > i; j--)
|
||||
else_break(&break_before[j],b_x);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
debug_output_breaks(break_before,count);
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
switch(break_class[i])
|
||||
{
|
||||
/* LB18 */
|
||||
case b_SP:
|
||||
if (i < count-1)
|
||||
else_break(&break_before[i+1],b_s);
|
||||
break;
|
||||
/* LB19 */
|
||||
case b_QU:
|
||||
else_break(&break_before[i],b_x);
|
||||
if (i < count-1)
|
||||
else_break(&break_before[i+1],b_x);
|
||||
break;
|
||||
/* LB20 */
|
||||
case b_CB:
|
||||
else_break(&break_before[i],b_s);
|
||||
if (i < count-1)
|
||||
else_break(&break_before[i+1],b_s);
|
||||
/* LB21 */
|
||||
case b_BA:
|
||||
case b_HY:
|
||||
case b_NS:
|
||||
else_break(&break_before[i],b_x);
|
||||
break;
|
||||
case b_BB:
|
||||
if (i < count-1)
|
||||
else_break(&break_before[i+1],b_x);
|
||||
break;
|
||||
/* LB22 */
|
||||
case b_IN:
|
||||
if (i > 0)
|
||||
{
|
||||
switch (break_class[i-1])
|
||||
{
|
||||
case b_AL:
|
||||
case b_ID:
|
||||
case b_IN:
|
||||
case b_NU:
|
||||
else_break(&break_before[i], b_x);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (i < count-1)
|
||||
{
|
||||
/* LB23 */
|
||||
if ((break_class[i] == b_ID && break_class[i+1] == b_PO) ||
|
||||
(break_class[i] == b_AL && break_class[i+1] == b_NU) ||
|
||||
(break_class[i] == b_NU && break_class[i+1] == b_AL))
|
||||
else_break(&break_before[i+1],b_x);
|
||||
/* LB24 */
|
||||
if ((break_class[i] == b_PR && break_class[i+1] == b_ID) ||
|
||||
(break_class[i] == b_PR && break_class[i+1] == b_AL) ||
|
||||
(break_class[i] == b_PO && break_class[i+1] == b_AL))
|
||||
else_break(&break_before[i+1],b_x);
|
||||
|
||||
/* LB25 */
|
||||
if ((break_class[i] == b_CL && break_class[i+1] == b_PO) ||
|
||||
(break_class[i] == b_CP && break_class[i+1] == b_PO) ||
|
||||
(break_class[i] == b_CL && break_class[i+1] == b_PR) ||
|
||||
(break_class[i] == b_CP && break_class[i+1] == b_PR) ||
|
||||
(break_class[i] == b_NU && break_class[i+1] == b_PO) ||
|
||||
(break_class[i] == b_NU && break_class[i+1] == b_PR) ||
|
||||
(break_class[i] == b_PO && break_class[i+1] == b_OP) ||
|
||||
(break_class[i] == b_PO && break_class[i+1] == b_NU) ||
|
||||
(break_class[i] == b_PR && break_class[i+1] == b_OP) ||
|
||||
(break_class[i] == b_PR && break_class[i+1] == b_NU) ||
|
||||
(break_class[i] == b_HY && break_class[i+1] == b_NU) ||
|
||||
(break_class[i] == b_IS && break_class[i+1] == b_NU) ||
|
||||
(break_class[i] == b_NU && break_class[i+1] == b_NU) ||
|
||||
(break_class[i] == b_SY && break_class[i+1] == b_NU))
|
||||
else_break(&break_before[i+1],b_x);
|
||||
|
||||
/* LB26 */
|
||||
if (break_class[i] == b_JL)
|
||||
{
|
||||
switch (break_class[i+1])
|
||||
{
|
||||
case b_JL:
|
||||
case b_JV:
|
||||
case b_H2:
|
||||
case b_H3:
|
||||
else_break(&break_before[i+1],b_x);
|
||||
}
|
||||
}
|
||||
if ((break_class[i] == b_JV || break_class[i] == b_H2) &&
|
||||
(break_class[i+1] == b_JV || break_class[i+1] == b_JT))
|
||||
else_break(&break_before[i+1],b_x);
|
||||
if ((break_class[i] == b_JT || break_class[i] == b_H3) &&
|
||||
break_class[i+1] == b_JT)
|
||||
else_break(&break_before[i+1],b_x);
|
||||
|
||||
/* LB27 */
|
||||
switch (break_class[i])
|
||||
{
|
||||
case b_JL:
|
||||
case b_JV:
|
||||
case b_JT:
|
||||
case b_H2:
|
||||
case b_H3:
|
||||
if (break_class[i+1] == b_IN || break_class[i+1] == b_PO)
|
||||
else_break(&break_before[i+1],b_x);
|
||||
}
|
||||
if (break_class[i] == b_PO)
|
||||
{
|
||||
switch (break_class[i+1])
|
||||
{
|
||||
case b_JL:
|
||||
case b_JV:
|
||||
case b_JT:
|
||||
case b_H2:
|
||||
case b_H3:
|
||||
else_break(&break_before[i+1],b_x);
|
||||
}
|
||||
}
|
||||
|
||||
/* LB28 */
|
||||
if (break_class[i] == b_AL && break_class[i+1] == b_AL)
|
||||
else_break(&break_before[i+1],b_x);
|
||||
|
||||
/* LB29 */
|
||||
if (break_class[i] == b_IS && break_class[i+1] == b_AL)
|
||||
else_break(&break_before[i+1],b_x);
|
||||
|
||||
/* LB30 */
|
||||
if ((break_class[i] == b_AL || break_class[i] == b_NU) &&
|
||||
break_class[i+1] == b_OP)
|
||||
else_break(&break_before[i+1],b_x);
|
||||
if (break_class[i] == b_CP &&
|
||||
(break_class[i+1] == b_AL || break_class[i] == b_NU))
|
||||
else_break(&break_before[i+1],b_x);
|
||||
}
|
||||
}
|
||||
debug_output_breaks(break_before,count);
|
||||
|
||||
/* LB31 */
|
||||
for (i = 0; i < count; i++)
|
||||
else_break(&break_before[i+1],b_s);
|
||||
|
||||
debug_output_breaks(break_before,count);
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
if (break_before[i] != b_x)
|
||||
{
|
||||
la[i].fSoftBreak = TRUE;
|
||||
la[i].fWordStop = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
HeapFree(GetProcessHeap(), 0, break_before);
|
||||
HeapFree(GetProcessHeap(), 0, break_class);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1512,26 +1512,12 @@ HRESULT WINAPI ScriptXtoCP(int iX,
|
|||
*/
|
||||
HRESULT WINAPI ScriptBreak(const WCHAR *chars, int count, const SCRIPT_ANALYSIS *sa, SCRIPT_LOGATTR *la)
|
||||
{
|
||||
int i;
|
||||
|
||||
TRACE("(%s, %d, %p, %p)\n", debugstr_wn(chars, count), count, sa, la);
|
||||
|
||||
if (!la) return S_FALSE;
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
memset(&la[i], 0, sizeof(SCRIPT_LOGATTR));
|
||||
BREAK_line(chars, count, sa, la);
|
||||
|
||||
/* FIXME: set the other flags */
|
||||
la[i].fWhiteSpace = (chars[i] == ' ');
|
||||
la[i].fCharStop = 1;
|
||||
|
||||
if (i > 0 && la[i - 1].fWhiteSpace)
|
||||
{
|
||||
la[i].fSoftBreak = 1;
|
||||
la[i].fWordStop = 1;
|
||||
}
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -130,3 +130,5 @@ void SHAPE_CharGlyphProp(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, const
|
|||
INT SHAPE_does_GSUB_feature_apply_to_chars(HDC hdc, SCRIPT_ANALYSIS *psa, ScriptCache* psc, const WCHAR *chars, INT write_dir, INT count, const char* feature) DECLSPEC_HIDDEN;
|
||||
|
||||
void Indic_ReorderCharacters( HDC hdc, SCRIPT_ANALYSIS *psa, ScriptCache* psc, LPWSTR input, int cChars, IndicSyllable **syllables, int *syllable_count, lexical_function lexical_f, reorder_function reorder_f, BOOL modern) DECLSPEC_HIDDEN;
|
||||
|
||||
void BREAK_line(const WCHAR *chars, int count, const SCRIPT_ANALYSIS *sa, SCRIPT_LOGATTR *la) DECLSPEC_HIDDEN;
|
||||
|
|
|
@ -115,6 +115,47 @@ my %ctype =
|
|||
"defin" => 0x0200
|
||||
);
|
||||
|
||||
my %break_types =
|
||||
(
|
||||
"BK" => 0x0001,
|
||||
"CR" => 0x0002,
|
||||
"LF" => 0x0003,
|
||||
"CM" => 0x0004,
|
||||
"SG" => 0x0005,
|
||||
"GL" => 0x0006,
|
||||
"CB" => 0x0007,
|
||||
"SP" => 0x0008,
|
||||
"ZW" => 0x0009,
|
||||
"NL" => 0x000a,
|
||||
"WJ" => 0x000b,
|
||||
"JL" => 0x000c,
|
||||
"JV" => 0x000d,
|
||||
"JT" => 0x000e,
|
||||
"H2" => 0x000f,
|
||||
"H3" => 0x0010,
|
||||
"XX" => 0x0011,
|
||||
"OP" => 0x0012,
|
||||
"CL" => 0x0013,
|
||||
"CP" => 0x0014,
|
||||
"QU" => 0x0015,
|
||||
"NS" => 0x0016,
|
||||
"EX" => 0x0017,
|
||||
"SY" => 0x0018,
|
||||
"IS" => 0x0019,
|
||||
"PR" => 0x001a,
|
||||
"PO" => 0x001b,
|
||||
"NU" => 0x001c,
|
||||
"AL" => 0x001d,
|
||||
"ID" => 0x001e,
|
||||
"IN" => 0x001f,
|
||||
"HY" => 0x0020,
|
||||
"BB" => 0x0021,
|
||||
"BA" => 0x0022,
|
||||
"SA" => 0x0023,
|
||||
"AI" => 0x0024,
|
||||
"B2" => 0x0025
|
||||
);
|
||||
|
||||
my %categories =
|
||||
(
|
||||
"Lu" => $ctype{"defin"}|$ctype{"alpha"}|$ctype{"upper"}, # Letter, Uppercase
|
||||
|
@ -960,6 +1001,54 @@ sub get_lb_ranges()
|
|||
return @ranges;
|
||||
}
|
||||
|
||||
################################################################
|
||||
# dump the Line Break Properties table
|
||||
sub dump_linebreak($)
|
||||
{
|
||||
my $filename = shift;
|
||||
my @break_table = ($break_types{'XX'}) x 65536;;
|
||||
my $next_group = 0;
|
||||
|
||||
my $INPUT = open_data_file "$UNIDATA/LineBreak.txt";
|
||||
while (<$INPUT>)
|
||||
{
|
||||
next if /^\#/; # skip comments
|
||||
next if /^\s*$/; # skip empty lines
|
||||
next if /\x1a/; # skip ^Z
|
||||
if (/^\s*([0-9a-fA-F]+)\s*;\s*([0-9A-Z][0-9A-Z])+\s*/)
|
||||
{
|
||||
my $type = $2;
|
||||
die "unknown breaktype $type" unless defined $break_types{$type};
|
||||
$break_table[hex $1] = $break_types{$type};
|
||||
next;
|
||||
}
|
||||
elsif (/^\s*([0-9a-fA-F]+)..\s*([0-9a-fA-F]+)\s*;\s*([0-9A-Z][0-9A-Z])+\s*/)
|
||||
{
|
||||
my $type = $3;
|
||||
die "unknown breaktype $type" unless defined $break_types{$type};
|
||||
foreach my $i (hex $1 .. hex $2)
|
||||
{
|
||||
$break_table[$i] = $break_types{$type};
|
||||
}
|
||||
next;
|
||||
}
|
||||
die "malformed line $_";
|
||||
}
|
||||
close $INPUT;
|
||||
|
||||
open OUTPUT,">$filename.new" or die "Cannot create $filename";
|
||||
print "Building $filename\n";
|
||||
print OUTPUT "/* Unicode Line Break Properties */\n";
|
||||
print OUTPUT "/* generated from $UNIDATA/LineBreak.txt */\n";
|
||||
print OUTPUT "/* DO NOT EDIT!! */\n\n";
|
||||
print OUTPUT "#include \"wine/unicode.h\"\n\n";
|
||||
|
||||
dump_simple_mapping( "wine_linebreak_table", @break_table);
|
||||
|
||||
close OUTPUT;
|
||||
save_file($filename);
|
||||
}
|
||||
|
||||
|
||||
################################################################
|
||||
# dump the BiDi mirroring table
|
||||
|
@ -1644,6 +1733,7 @@ DUMP_COMPOSE_TABLES( "compose.c" );
|
|||
DUMP_CTYPE_TABLES( "wctype.c" );
|
||||
dump_mirroring( "../../dlls/usp10/mirror.c" );
|
||||
dump_shaping( "../../dlls/usp10/shaping.c" );
|
||||
dump_linebreak( "../../dlls/usp10/linebreak.c" );
|
||||
dump_intl_nls("../../tools/l_intl.nls");
|
||||
|
||||
foreach my $file (@allfiles) { HANDLE_FILE( @{$file} ); }
|
||||
|
|
Loading…
Reference in New Issue