Commit 397c3ac5 authored by Aric Stewart's avatar Aric Stewart Committed by Alexandre Julliard

usp10: Improve Sinhala shaping using Indic rules.

parent 301c6ef4
......@@ -4,6 +4,7 @@ IMPORTS = gdi32
C_SRCS = \
bidi.c \
indic.c \
mirror.c \
shape.c \
shaping.c \
......
/*
* Implementation of Indic Syllables 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);
static void debug_output_string(LPCWSTR str, int cChar, lexical_function f)
{
int i;
if (TRACE_ON(uniscribe))
{
for (i = 0; i < cChar; i++)
{
switch (f(str[i]))
{
case lex_Consonant: TRACE("C"); break;
case lex_Ra: TRACE("Ra"); break;
case lex_Vowel: TRACE("V"); break;
case lex_Nukta: TRACE("N"); break;
case lex_Halant: TRACE("H"); break;
case lex_ZWNJ: TRACE("Zwnj"); break;
case lex_ZWJ: TRACE("Zwj"); break;
case lex_Mantra_post: TRACE("Mp");break;
case lex_Mantra_above: TRACE("Ma");break;
case lex_Mantra_below: TRACE("Mb");break;
case lex_Mantra_pre: TRACE("Mm");break;
case lex_Modifier: TRACE("Sm"); break;
case lex_Vedic: TRACE("Vd"); break;
case lex_Anudatta: TRACE("A"); break;
case lex_Composed_Vowel: TRACE("t"); break;
default:
TRACE("X"); break;
}
}
TRACE("\n");
}
}
static inline BOOL is_consonant( int type )
{
return (type == lex_Ra || type == lex_Consonant);
}
static inline BOOL is_mantra( int type )
{
return (type == lex_Mantra_above || type == lex_Mantra_below ||
type == lex_Mantra_pre || type == lex_Mantra_post);
}
static inline BOOL is_joiner( int type )
{
return (type == lex_ZWJ || type == lex_ZWNJ);
}
static INT consonant_header(LPCWSTR input, INT cChar, INT start, INT next,
lexical_function lex)
{
if (!is_consonant( lex(input[next]) )) return -1;
next++;
if ((next < cChar) && lex(input[next]) == lex_Nukta)
next++;
if (lex(input[next])==lex_Halant)
{
next++;
if((next < cChar) && is_joiner( lex(input[next]) ))
next++;
if ((next < cChar) && is_consonant( lex(input[next]) ))
return next;
}
else if (is_joiner( lex(input[next]) ) && lex(input[next+1])==lex_Halant)
{
next+=2;
if ((next < cChar) && is_consonant( lex(input[next]) ))
return next;
}
return -1;
}
static INT parse_consonant_syllable(LPCWSTR input, INT cChar, INT start,
INT *main, INT next, lexical_function lex)
{
int check;
int headers = 0;
do
{
check = consonant_header(input,cChar,start,next,lex);
if (check != -1)
{
next = check;
headers++;
}
} while (check != -1);
if (headers || is_consonant( lex(input[next]) ))
{
*main = next;
next++;
}
else
return -1;
if ((next < cChar) && lex(input[next]) == lex_Nukta)
next++;
if ((next < cChar) && lex(input[next]) == lex_Anudatta)
next++;
if ((next < cChar) && lex(input[next]) == lex_Halant)
{
next++;
if((next < cChar) && is_joiner( lex(input[next]) ))
next++;
}
else if (next < cChar)
{
while((next < cChar) && is_mantra( lex(input[next]) ))
next++;
if ((next < cChar) && lex(input[next]) == lex_Nukta)
next++;
if ((next < cChar) && lex(input[next]) == lex_Halant)
next++;
}
if ((next < cChar) && lex(input[next]) == lex_Modifier)
next++;
if ((next < cChar) && lex(input[next]) == lex_Vedic)
next++;
return next;
}
static INT parse_vowel_syllable(LPCWSTR input, INT cChar, INT start,
INT next, lexical_function lex)
{
if ((next < cChar) && lex(input[next]) == lex_Nukta)
next++;
if ((next < cChar) && is_joiner( lex(input[next]) ) && lex(input[next+1])==lex_Halant && is_consonant( lex(input[next+2]) ))
next+=3;
else if ((next < cChar) && lex(input[next])==lex_Halant && is_consonant( lex(input[next+1]) ))
next+=2;
else if ((next < cChar) && lex(input[next])==lex_ZWJ && is_consonant( lex(input[next+1]) ))
next+=2;
if (is_mantra( lex(input[next]) ))
{
while((next < cChar) && is_mantra( lex(input[next]) ))
next++;
if ((next < cChar) && lex(input[next]) == lex_Nukta)
next++;
if ((next < cChar) && lex(input[next]) == lex_Halant)
next++;
}
if ((next < cChar) && lex(input[next]) == lex_Modifier)
next++;
if ((next < cChar) && lex(input[next]) == lex_Vedic)
next++;
return next;
}
static INT Indic_process_next_syllable( LPCWSTR input, INT cChar, INT start, INT* main, INT next, lexical_function lex )
{
if (lex(input[next])==lex_Vowel)
{
*main = next;
return parse_vowel_syllable(input, cChar, start, next+1, lex);
}
else if ((cChar > next+3) && lex(input[next]) == lex_Ra && lex(input[next+1]) == lex_Halant && lex(input[next+2]) == lex_Vowel)
{
*main = next+2;
return parse_vowel_syllable(input, cChar, start, next+3, lex);
}
else if (start == next && lex(input[next])==lex_NBSP)
{
*main = next;
return parse_vowel_syllable(input, cChar, start, next+1, lex);
}
else if (start == next && (cChar > next+3) && lex(input[next]) == lex_Ra && lex(input[next+1]) == lex_Halant && lex(input[next+2]) == lex_NBSP)
{
*main = next+2;
return parse_vowel_syllable(input, cChar, start, next+3, lex);
}
return parse_consonant_syllable(input, cChar, start, main, next, lex);
}
void Indic_ReorderCharacters( LPWSTR input, int cChar, lexical_function lex, reorder_function reorder_f)
{
int index = 0;
int next = 0;
int center = 0;
if (!lex || ! reorder_f)
{
ERR("Failure to have required functions\n");
return;
}
debug_output_string(input, cChar, lex);
while (next != -1)
{
while((next < cChar) && lex(input[next]) == lex_Generic)
next++;
index = next;
next = Indic_process_next_syllable(input, cChar, 0, &center, index, lex);
if (next != -1)
{
reorder_f(input, index, center, next-1, lex);
index = next;
}
else if (index < cChar)
{
int i;
TRACE("Processing failed at %i\n",index);
for (i = index; i < cChar; i++)
if (lex(input[i])==lex_Generic)
{
TRACE("Restart processing at %i\n",i);
next = i;
index = i;
break;
}
}
}
TRACE("Processed %i of %i characters\n",index,cChar);
}
int Indic_FindBaseConsonant(LPWSTR input, INT start, INT main, INT end, lexical_function lex)
{
int i;
/* try to find a base consonant */
if (!is_consonant( lex(input[main]) ))
{
for (i = end; i >= start; i--)
if (is_consonant( lex(input[i]) ))
{
main = i;
break;
}
}
return main;
}
......@@ -43,6 +43,7 @@ typedef VOID (*ContextualShapingProc)(HDC, ScriptCache*, SCRIPT_ANALYSIS*,
static void ContextualShape_Arabic(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust);
static void ContextualShape_Syriac(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust);
static void ContextualShape_Phags_pa(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust);
static void ContextualShape_Sinhala(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust);
typedef VOID (*ShapeCharGlyphPropProc)( HDC , ScriptCache*, SCRIPT_ANALYSIS*, const WCHAR*, const INT, const WORD*, const INT, WORD*, SCRIPT_CHARPROP*, SCRIPT_GLYPHPROP*);
......@@ -279,6 +280,12 @@ typedef struct {
static INT GSUB_apply_lookup(const GSUB_LookupList* lookup, INT lookup_index, WORD *glyphs, INT glyph_index, INT write_dir, INT *glyph_count);
typedef struct tagVowelComponents
{
WCHAR base;
WCHAR parts[3];
} VowelComponents;
/* the orders of joined_forms and contextual_features need to line up */
static const char* contextual_features[] =
{
......@@ -400,7 +407,7 @@ static const ScriptShapeData ShapingData[] =
{{ standard_features, 2}, NULL, "cyrl", "", NULL, NULL},
{{ standard_features, 2}, NULL, "armn", "", NULL, NULL},
{{ standard_features, 2}, NULL, "geor", "", NULL, NULL},
{{ sinhala_features, 7}, NULL, "sinh", "", NULL, NULL},
{{ sinhala_features, 7}, NULL, "sinh", "", ContextualShape_Sinhala, NULL},
{{ tibetan_features, 2}, NULL, "tibt", "", NULL, ShapeCharGlyphProp_Tibet},
{{ tibetan_features, 2}, NULL, "tibt", "", NULL, ShapeCharGlyphProp_Tibet},
{{ tibetan_features, 2}, NULL, "phag", "", ContextualShape_Phags_pa, ShapeCharGlyphProp_Thai},
......@@ -1493,6 +1500,160 @@ static void ContextualShape_Phags_pa(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS
HeapFree(GetProcessHeap(),0,context_shape);
}
static void ReplaceInsertChars(HDC hdc, INT cWalk, INT* pcChars, WCHAR *pwOutChars, const WCHAR *replacements)
{
int i;
/* Replace */
pwOutChars[cWalk] = replacements[0];
cWalk=cWalk+1;
/* Insert */
for (i = 1; replacements[i] != 0x0000 && i < 3; i++)
{
int j;
for (j = *pcChars; j > cWalk; j--)
pwOutChars[j] = pwOutChars[j-1];
*pcChars= *pcChars+1;
pwOutChars[cWalk] = replacements[i];
cWalk = cWalk+1;
}
}
static void DecomposeVowels(HDC hdc, WCHAR *pwOutChars, INT *pcChars, const VowelComponents vowels[])
{
int i;
int cWalk;
for (cWalk = 0; cWalk < *pcChars; cWalk++)
{
for (i = 0; vowels[i].base != 0x0; i++)
{
if (pwOutChars[cWalk] == vowels[i].base)
{
ReplaceInsertChars(hdc, cWalk, pcChars, pwOutChars, vowels[i].parts);
break;
}
}
}
}
static void Reorder_Ra_follows_base(LPWSTR pwChar, INT start, INT main, INT end, lexical_function lexical)
{
if (start != main && end > start+1 && lexical(pwChar[start]) == lex_Ra && lexical(pwChar[start+1]) == lex_Halant)
{
int j;
WORD Ra = pwChar[start];
WORD H = pwChar[start+1];
TRACE("Doing reorder of Ra to %i\n",main);
for (j = start; j < main-1; j++)
pwChar[j] = pwChar[j+2];
pwChar[main-1] = Ra;
pwChar[main] = H;
}
}
static void Reorder_Mantra_precede_base(LPWSTR pwChar, INT start, INT main, INT end, lexical_function lexical)
{
int i;
/* reorder Mantras */
if (end > main)
{
for (i = 1; i <= end-main; i++)
{
if (lexical(pwChar[main+i]) == lex_Mantra_pre)
{
int j;
WCHAR c = pwChar[main+i];
TRACE("Doing reorder of %x %x\n",c,pwChar[main]);
for (j = main+i; j > main; j--)
pwChar[j] = pwChar[j-1];
pwChar[main] = c;
}
}
}
}
static void Reorder_Like_Sinhala(LPWSTR pwChar, INT start, INT main, INT end, lexical_function lexical)
{
TRACE("Syllable (%i..%i..%i)\n",start,main,end);
if (start == main && main == end) return;
main = Indic_FindBaseConsonant(pwChar, start, main, end, lexical);
if (lexical(pwChar[main]) == lex_Vowel) return;
Reorder_Ra_follows_base(pwChar, start, main, end, lexical);
Reorder_Mantra_precede_base(pwChar, start, main, end, lexical);
}
static int sinhala_lex(WCHAR c)
{
switch (c)
{
case 0x0DCA: return lex_Halant;
case 0x0DCF:
case 0x0DDF:
case 0x0DD8: return lex_Mantra_post;
case 0x0DD9:
case 0x0DDB: return lex_Mantra_pre;
case 0x0DDA:
case 0x0DDC: return lex_Composed_Vowel;
case 0x200D: return lex_ZWJ;
case 0x200C: return lex_ZWNJ;
case 0x00A0: return lex_NBSP;
default:
if (c>=0x0D82 && c <=0x0D83) return lex_Modifier;
else if (c>=0x0D85 && c <=0x0D96) return lex_Vowel;
else if (c>=0x0D96 && c <=0x0DC6) return lex_Consonant;
else if (c>=0x0DD0 && c <=0x0DD1) return lex_Mantra_post;
else if (c>=0x0DD2 && c <=0x0DD3) return lex_Mantra_above;
else if (c>=0x0DD4 && c <=0x0DD6) return lex_Mantra_below;
else if (c>=0x0DDD && c <=0x0DDE) return lex_Composed_Vowel;
else if (c>=0x0DF2 && c <=0x0DF3) return lex_Mantra_post;
else return lex_Generic;
}
}
static const VowelComponents Sinhala_vowels[] = {
{0x0DDA, {0x0DD9,0x0DCA,0x0}},
{0x0DDC, {0x0DD9,0x0DCF,0x0}},
{0x0DDD, {0x0DD9,0x0DCF,0x0DCA}},
{0x0DDE, {0x0DD9,0x0DDF,0x0}},
{0x0000, {0x0000,0x0000,0x0}}};
static void ContextualShape_Sinhala(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, WORD *pwLogClust)
{
int cCount = cChars;
WCHAR *input;
if (*pcGlyphs != cChars)
{
ERR("Number of Glyphs and Chars need to match at the beginning\n");
return;
}
input = HeapAlloc(GetProcessHeap(),0,sizeof(WCHAR) * (cChars * 3));
memcpy(input, pwcChars, cChars * sizeof(WCHAR));
/* Step 1: Decompose multi part vowels */
DecomposeVowels(hdc, input, &cCount, Sinhala_vowels);
TRACE("New double vowel expanded string %s (%i)\n",debugstr_wn(input,cCount),cCount);
/* Step 2: Reorder within Syllables */
Indic_ReorderCharacters( input, cCount, sinhala_lex, Reorder_Like_Sinhala);
TRACE("reordered string %s\n",debugstr_wn(input,cCount));
/* Step 3: Get glyphs */
GetGlyphIndicesW(hdc, input, cCount, pwOutGlyphs, 0);
*pcGlyphs = cCount;
HeapFree(GetProcessHeap(),0,input);
}
static void ShapeCharGlyphProp_Default( HDC hdc, ScriptCache* psc, SCRIPT_ANALYSIS* psa, const WCHAR* pwcChars, const INT cChars, const WORD* pwGlyphs, const INT cGlyphs, WORD* pwLogClust, SCRIPT_CHARPROP* pCharProp, SCRIPT_GLYPHPROP* pGlyphProp)
{
int i,k;
......
......@@ -74,6 +74,11 @@ typedef struct {
OPENTYPE_TAG userLang;
} ScriptCache;
enum {lex_Halant, lex_Composed_Vowel, lex_Mantra_post, lex_Mantra_pre, lex_Mantra_above, lex_Mantra_below, lex_ZWJ, lex_ZWNJ, lex_NBSP, lex_Modifier, lex_Vowel, lex_Consonant, lex_Generic, lex_Ra, lex_Vedic, lex_Anudatta, lex_Nukta};
typedef int (*lexical_function)(WCHAR c);
typedef void (*reorder_function)(LPWSTR pwChar, INT start, INT main, INT end, lexical_function lex);
#define odd(x) ((x) & 1)
BOOL BIDI_DetermineLevels( LPCWSTR lpString, INT uCount, const SCRIPT_STATE *s,
......@@ -86,3 +91,6 @@ void SHAPE_ContextualShaping(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WC
void SHAPE_ApplyDefaultOpentypeFeatures(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs, INT cChars, WORD *pwLogClust) DECLSPEC_HIDDEN;
HRESULT SHAPE_CheckFontForRequiredFeatures(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa) DECLSPEC_HIDDEN;
void SHAPE_CharGlyphProp(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, const WCHAR* pwcChars, const INT cChars, const WORD* pwGlyphs, const INT cGlyphs, WORD *pwLogClust, SCRIPT_CHARPROP *pCharProp, SCRIPT_GLYPHPROP *pGlyphProp) DECLSPEC_HIDDEN;
void Indic_ReorderCharacters( LPWSTR input, int cChars, lexical_function lexical_f, reorder_function reorder_f) DECLSPEC_HIDDEN;
int Indic_FindBaseConsonant(LPWSTR pwChar, INT start, INT main, INT end, lexical_function lex) DECLSPEC_HIDDEN;
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment