// Copyright (c) 2003-2009 Nokia Corporation and/or its subsidiary(-ies).
// All rights reserved.
// This component and the accompanying materials are made available
// under the terms of "Eclipse Public License v1.0"
// which accompanies this distribution, and is available
// at the URL "http://www.eclipse.org/legal/epl-v10.html".
//
// Initial Contributors:
// Nokia Corporation - initial contribution.
//
// Contributors:
//
// Description:
//
/**
@file
@internalComponent
*/
#include <gdi.h>
#include "FontThai.h"
//
// ThaiGlyph Namespace definition
//
/**
This namespace holds functions used to evaluate a glyph character code
against a given Thai related prediciate. The 'code' argument is a glyph
from the current output cluster and so may be a Thai glyph, Thai PUA glyph,
the dotted circle glyph or 0xffff. Therefore it was decided not to implement
these routines using a data driven table approach as it would be inefficient.
@internalComponent.
*/
namespace ThaiGlyph
{
const TText16 KYoYing = 0x0E0D;
const TText16 KYoYingPua = 0xF70F;
const TText16 KThoThan = 0x0E10;
const TText16 KThoThanPua = 0xF700;
const TText16 KNikhahit = 0x0E4D;
const TText16 KNikhahitPua = 0xF711;
const TText16 KSaraAa = 0x0E32;
const TText16 KSaraAm = 0x0E33;
TBool IsThaiGlyph(TUint code)
{
return ((code > 0x0E00 && code < 0x0E3B) ||
(code > 0x0E3E && code < 0x0E5C) ||
(code >= 0xF700 && code <= 0xF71A));
}
TBool IsThaiConsonant(TUint code)
{
return (code >= 0x0E01 && code <= 0x0E2E);
}
TBool IsThaiTallConsonant(TUint code)
{
return (//code == 0x0E0A || // CHO CHANG not tall at all
//code == 0x0E0B || // SO SO not tall at all
code == 0x0E1B || // PO PLA
code == 0x0E1D || // FO FA
code == 0x0E1F || // FO FAN
code == 0x0E2C); // LO CHULA in some fonts, Unicode tables show it as tall
}
TBool IsThaiShortConsonant(TUint code)
{
return (((code >= 0x0E01 && code <= 0x0E2E) || (code == KUnicodeDottedCircle)) &&
code != 0x0E1B && // PO PLA
code != 0x0E1D && // FO FA
code != 0x0E1F && // FO FAN
code != 0x0E2C); // LO CHULA in some fonts, Unicode tables show it as tall
}
TBool IsThaiConsonantWithDisjointDescender(TUint code)
{
return (code == ThaiGlyph::KYoYing || code == ThaiGlyph::KThoThan);
}
TBool IsThaiConsonantWithJointDescender(TUint code)
{
return (code == 0x0E0E || // DO CHADA
code == 0x0E0F || // PO PATAK
code == 0x0E24 || // RU
code == 0x0E26); // LU
}
TBool IsThaiVowel(TUint code)
{
return ((code >= 0x0E30 && code <= 0x0E3A) ||
(code >= 0x0E40 && code <= 0x0E44) ||
code == 0x0E47); // MAITAIKHU
}
TBool IsThaiDepVowel(TUint code)
{
return (code == 0x0E31 || // MAI HAN-AKAT
(code >= 0x0E34 && code <= 0x0E3A) ||
code == 0x0E47); // MAITAIKHU
}
TBool IsThaiDepVowelAbove(TUint code)
{
return (code == 0x0E31 || // MAI HAN-AKAT
(code >= 0x0E34 && code <= 0x0E37) ||
code == 0x0E47); // MAITAIKHU
}
TBool IsThaiDepVowelAbovePUA(TUint code)
{
return (code == 0xF710 || // MAI HAN-AKAT
(code >= 0xF701 && code <= 0xF704) ||
code == 0xF712); // MAITAIKHU
}
TBool IsThaiDepVowelBelow(TUint code)
{
return (code >= 0x0E38 && code <= 0x0E3A);
}
TBool IsThaiIndepVowel(TUint code)
{
return (code == 0x0E30 || // SARA A
code == 0x0E32 || // SARA AA
code == 0x0E33 || // SARA AM
(code >= 0x0E40 && code <= 0x0E44));
}
TBool IsThaiToneMark(TUint code)
{
return (code >= 0x0E48 && code <= 0x0E4B);
}
}
//
//
// ThaiCharRules Namespace definition
//
//
/**
ThaiCharRules namespace holds the data and lookup methods
implementing the WTT 2.0 input/output validation matrix.
@internalComponent
*/
namespace ThaiCharRules
{
const TUint KThaiCodePageStart = 0x0E00;
const TUint KThaiCodePageEnd = 0x0E5C;
const TUint KNumThaiCharacters = KThaiCodePageEnd-KThaiCodePageStart;
enum Wtt2Rule
{
EUndefined,
EAccept,
EComposite,
EReject,
ERejectStrict,
};
/**
This enumeration holds the set of classification values a Thai
character can be categorised as in the WTT2.0 specification.
*/
enum CharClassification
{
ENull,
EControl,
ENonPrintable,
EConsonant,
ELeadingVowel,
EOrdinaryFollowingVowel,
EDependentFollowingVowel,
ESpecialFollowingVowel,
EShortBelowVowel,
ELongBelowVowel,
EBelowDiacritic,
EToneMark,
EAboveDiacritic0,
EAboveDiacritic1,
EAboveDiacritic2,
EAboveDiacritic3,
EAboveVowel1,
EAboveVowel2,
EAboveVowel3,
// marker for end
EMaxClassification
};
/**
Data table holding the classification of each character.
*/
static const TUint8 iCharClassifications[KNumThaiCharacters] =
{
ENull, // No entry in code page
EConsonant, // 0x0E01
EConsonant, // 0x0E02
EConsonant, // 0x0E03
EConsonant, // 0x0E04
EConsonant, // 0x0E05
EConsonant, // 0x0E06
EConsonant, // 0x0E07
EConsonant, // 0x0E08
EConsonant, // 0x0E09
EConsonant, // 0x0E0A
EConsonant, // 0x0E0B
EConsonant, // 0x0E0C
EConsonant, // 0x0E0D
EConsonant, // 0x0E0E
EConsonant, // 0x0E0F
EConsonant, // 0x0E10
EConsonant, // 0x0E11
EConsonant, // 0x0E12
EConsonant, // 0x0E13
EConsonant, // 0x0E14
EConsonant, // 0x0E15
EConsonant, // 0x0E16
EConsonant, // 0x0E17
EConsonant, // 0x0E18
EConsonant, // 0x0E19
EConsonant, // 0x0E1A
EConsonant, // 0x0E1B
EConsonant, // 0x0E1C
EConsonant, // 0x0E1D
EConsonant, // 0x0E1E
EConsonant, // 0x0E1F
EConsonant, // 0x0E20
EConsonant, // 0x0E21
EConsonant, // 0x0E22
EConsonant, // 0x0E23
EConsonant, // 0x0E24
EConsonant, // 0x0E25
EConsonant, // 0x0E26
EConsonant, // 0x0E27
EConsonant, // 0x0E28
EConsonant, // 0x0E29
EConsonant, // 0x0E2A
EConsonant, // 0x0E2B
EConsonant, // 0x0E2C
EConsonant, // 0x0E2D
EConsonant, // 0x0E2E
ENonPrintable, // 0x0E2F
EOrdinaryFollowingVowel,// 0x0E30
EAboveVowel2, // 0x0E31
EOrdinaryFollowingVowel,// 0x0E32
EOrdinaryFollowingVowel,// 0x0E33
EAboveVowel1, // 0x0E34
EAboveVowel3, // 0x0E35
EAboveVowel2, // 0x0E36
EAboveVowel3, // 0x0E37
EShortBelowVowel, // 0x0E38
ELongBelowVowel, // 0x0E39
EBelowDiacritic, // 0x0E3A
ENull, // 0x0E3B
ENull, // 0x0E3C
ENull, // 0x0E3D
ENull, // 0x0E3E
ENonPrintable, // 0x0E3F
ELeadingVowel, // 0x0E40
ELeadingVowel, // 0x0E41
ELeadingVowel, // 0x0E42
ELeadingVowel, // 0x0E43
ELeadingVowel, // 0x0E44
EDependentFollowingVowel,//0x0E45
ENonPrintable, // 0x0E46
EAboveDiacritic2, // 0x0E47
EToneMark, // 0x0E48
EToneMark, // 0x0E49
EToneMark, // 0x0E4A
EToneMark, // 0x0E4B
EAboveDiacritic1, // 0x0E4C
EAboveDiacritic0, // 0x0E4D
EAboveDiacritic3, // 0x0E4E
ENonPrintable, // 0x0E4F
ENonPrintable, // 0x0E50
ENonPrintable, // 0x0E51
ENonPrintable, // 0x0E52
ENonPrintable, // 0x0E53
ENonPrintable, // 0x0E54
ENonPrintable, // 0x0E55
ENonPrintable, // 0x0E56
ENonPrintable, // 0x0E57
ENonPrintable, // 0x0E58
ENonPrintable, // 0x0E59
ENonPrintable, // 0x0E5A
ENonPrintable, // 0x0E5B
// Value at last measurement was 92 bytes. 27/6/2003
};
/**
WTT 2.0 Rules data table of prev to next character
*/
static const TUint8 iInputRules[EMaxClassification][EMaxClassification] =
{
/* Previous character ENull */
{
EUndefined, EUndefined, EUndefined, EUndefined, EUndefined,
EUndefined, EUndefined, EUndefined, EUndefined, EUndefined,
EUndefined, EUndefined, EUndefined, EUndefined, EUndefined,
EUndefined, EUndefined, EUndefined, EUndefined
},
/* Previous character EControl */
{
EUndefined, EUndefined, EAccept, EAccept, EAccept,
EAccept, EAccept, EAccept, EReject, EReject,
EReject, EReject, EReject, EReject, EReject, EReject,
EReject, EReject, EReject,
},
/* Previous character ENonPrintable */
{
EUndefined, EUndefined, EAccept, EAccept, EAccept,
ERejectStrict, ERejectStrict, EAccept, EReject, EReject,
EReject, EReject, EReject, EReject, EReject, EReject,
EReject, EReject, EReject,
},
/* Previous character EConsonant */
{
EUndefined, EUndefined, EAccept, EAccept, EAccept,
EAccept, ERejectStrict, EAccept, EComposite, EComposite,
EComposite, EComposite, EComposite, EComposite, EComposite, EComposite,
EComposite, EComposite, EComposite,
},
/* Previous character ELeadingVowel */
{
EUndefined, EUndefined, ERejectStrict, EAccept, ERejectStrict,
ERejectStrict, ERejectStrict, ERejectStrict, EReject, EReject,
EReject, EReject, EReject, EReject, EReject, EReject,
EReject, EReject, EReject,
},
/* Previous character EOrdinaryFollowingVowel */
{
EUndefined, EUndefined, ERejectStrict, EAccept, ERejectStrict,
EAccept, ERejectStrict, EAccept, EReject, EReject,
EReject, EReject, EReject, EReject, EReject, EReject,
EReject, EReject, EReject,
},
/* Previous character EDependentFollowingVowel */
{
EUndefined, EUndefined, EAccept, EAccept, EAccept,
EAccept, ERejectStrict, EAccept, EReject, EReject,
EReject, EReject, EReject, EReject, EReject, EReject,
EReject, EReject, EReject,
},
/* Previous character ESpecialFollowingVowel */
{
EUndefined, EUndefined, EAccept, EAccept, EAccept,
ERejectStrict, EAccept, ERejectStrict, EReject, EReject,
EReject, EReject, EReject, EReject, EReject, EReject,
EReject, EReject, EReject,
},
/* Previous character EShortBelowVowel */
{
EUndefined, EUndefined, EAccept, EAccept, EAccept,
EAccept, ERejectStrict, EAccept, EReject, EReject,
EReject, EComposite, EComposite, EComposite, EReject, EReject,
EReject, EReject, EReject,
},
/* Previous character ELongBelowVowel */
{
EUndefined, EUndefined, EAccept, EAccept, EAccept,
ERejectStrict, ERejectStrict, EAccept, EReject, EReject,
EReject, EComposite, EReject, EReject, EReject, EReject,
EReject, EReject, EReject,
},
/* Previous character EBelowDiacritic */
{
EUndefined, EUndefined, EAccept, EAccept, EAccept,
ERejectStrict, ERejectStrict, EAccept, EReject, EReject,
EReject, EReject, EReject, EReject, EReject, EReject,
EReject, EReject, EReject,
},
/* Previous character EToneMark */
{
EUndefined, EUndefined, EAccept, EAccept, EAccept,
EAccept, EAccept, EAccept, EReject, EReject,
EReject, EReject, EReject, EReject, EReject, EReject,
EReject, EReject, EReject,
},
/* Previous character EAboveDiacritic0 */
{
EUndefined, EUndefined, EAccept, EAccept, EAccept,
ERejectStrict, ERejectStrict, EAccept, EReject, EReject,
EReject, EReject, EReject, EReject, EReject, EReject,
EReject, EReject, EReject,
},
/* Previous character EAboveDiacritic1 */
{
EUndefined, EUndefined, EAccept, EAccept, EAccept,
ERejectStrict, ERejectStrict, EAccept, EReject, EReject,
EReject, EReject, EReject, EReject, EReject, EReject,
EReject, EReject, EReject,
},
/* Previous character EAboveDiacritic2 */
{
EUndefined, EUndefined, EAccept, EAccept, EAccept,
ERejectStrict, ERejectStrict, EAccept, EReject, EReject,
EReject, EReject, EReject, EReject, EReject, EReject,
EReject, EReject, EReject,
},
/* Previous character EAboveDiacritic3 */
{
EUndefined, EUndefined, EAccept, EAccept, EAccept,
ERejectStrict, ERejectStrict, EAccept, EReject, EReject,
EReject, EReject, EReject, EReject, EReject, EReject,
EReject, EReject, EReject,
},
/* Previous character EAboveVowel1 */
{
EUndefined, EUndefined, EAccept, EAccept, EAccept,
ERejectStrict, ERejectStrict, EAccept, EReject, EReject,
EReject, EComposite, EReject, EComposite, EReject, EReject,
// EReject, EComposite, EComposite, EComposite, EReject, EReject,
EReject, EReject, EReject,
},
/* Previous character EAboveVowel2 */
{
EUndefined, EUndefined, EAccept, EAccept, EAccept,
ERejectStrict, ERejectStrict, EAccept, EReject, EReject,
EReject, EComposite, EReject, EReject, EReject, EReject,
EReject, EReject, EReject,
},
/* Previous character EAboveVowel3 */
{
EUndefined, EUndefined, EAccept, EAccept, EAccept,
ERejectStrict, ERejectStrict, EAccept, EReject, EReject,
EReject, EComposite, EReject, EReject, EReject, EReject,
// EReject, EComposite, EReject, EComposite, EReject,
EReject, EReject, EReject,
},
// Value at last measurement was 324 bytes. 27/6/2003
};
/**
This routine looks up the WTT 2.0 rule for the given
Thai character codes provided in the WTT 2.0 data table.
@param aPrevChar
Unicode character code preceding the assumed position.
@param aChar
Unicode character code proceeding the assumed position.
@return Wtt2Rule
The rule value found in data table.
*/
Wtt2Rule LookupWtt2Rule(TUint aPrevChar, TUint aChar)
{
const CharClassification prevCharClassification =
static_cast<CharClassification>(
(aPrevChar > KThaiCodePageStart && aPrevChar < KThaiCodePageEnd) ?
iCharClassifications[aPrevChar - KThaiCodePageStart] :
ENonPrintable);
const CharClassification charClassification =
static_cast<CharClassification>(
(aChar > KThaiCodePageStart && aChar < KThaiCodePageEnd) ?
iCharClassifications[aChar - KThaiCodePageStart] :
ENonPrintable);
return static_cast<Wtt2Rule>
(iInputRules[prevCharClassification][charClassification]);
}
}
using namespace ThaiCharRules;
//
//
// ThaiGlyphPUASubstitution Namespace definition
//
//
/**
This utility namespace holds the data and lookup mechanisms to support
the GlyphSelector_Thai glyph selection class in choosing Private User
Area (PUA) Thai character positional variant glyphs. Use of the PUA glyphs
results in a satisfactory rendition of Thai writing in Symbian OS.
@internalComponent
*/
namespace ThaiGlyphPUASubstitution
{
typedef TBool (*UnicodeCharValidator)(const TGlyphSelectionState& aGss);
struct PUASubstTableEntry
{
TUint iOrigGlyph;
TUint iPUAGlyph;
UnicodeCharValidator iRuleFunc;
};
/**
ThaiGlyphPUASubstitution rule method which checks the context for
short consonant preceding char OR
short consonant & dependent vowel below preceding char.
@param aGss
Container object holds the glyph selection context for the method.
@return TBool
ETrue when the rule context is satisfied, EFalse if not.
*/
TBool RuleShortConsonant(const TGlyphSelectionState& aGss)
{
if (aGss.iParam.iOutputGlyphs == 1) {
//check the context for short consonant preceding char
TUint consonantGss = aGss.iParam.iOutput[0].iCode;
if (ThaiGlyph::IsThaiShortConsonant(consonantGss) ||
consonantGss == ThaiGlyph::KYoYingPua ||
consonantGss == ThaiGlyph::KThoThanPua)
return ETrue;
else
return EFalse;
}
if (aGss.iParam.iOutputGlyphs == 2) {
//check the context for short consonant & dependent vowel below preceding char
TUint consonantGss = aGss.iParam.iOutput[0].iCode;
TUint depVowelGss = aGss.iParam.iOutput[1].iCode;
if ((ThaiGlyph::IsThaiShortConsonant(consonantGss) ||
consonantGss == ThaiGlyph::KYoYingPua ||
consonantGss == ThaiGlyph::KThoThanPua) &&
(ThaiGlyph::IsThaiDepVowelBelow(depVowelGss) ||
(depVowelGss >= 0xF718 &&
depVowelGss <= 0xF71A)))
return ETrue;
else
return EFalse;
}
return EFalse;
}
/**
ThaiGlyphPUASubstitution rule method which checks the context for
tall consonant preceding char.
@param aGss
Container object holds the glyph selection context for the method.
@return TBool
ETrue when the rule context is satisfied, EFalse if not.
*/
TBool RuleTallConsonant(const TGlyphSelectionState& aGss)
{
if ((aGss.iParam.iOutputGlyphs == 1) &&
ThaiGlyph::IsThaiTallConsonant(aGss.iParam.iOutput[0].iCode))
return ETrue;
else
return EFalse;
}
/**
ThaiGlyphPUASubstitution rule method which checks the context for a tall
consonant which does not have a dependent vowel above or a nikhahit or a
following sara am.
@param aGss
Container object holds the glyph selection context for the method.
@return TBool
ETrue when the rule context is satisfied, EFalse if not.
*/
TBool RuleTallConsonantNoVowelAbove(const TGlyphSelectionState& aGss)
{
if (aGss.iParam.iOutputGlyphs == 0)
return EFalse;
if (!ThaiGlyph::IsThaiTallConsonant(aGss.iParam.iOutput[0].iCode))
return EFalse;
if (aGss.iParam.iOutputGlyphs == 1)
return ETrue;
if (aGss.iParam.iOutputGlyphs != 2)
return EFalse;
TUint wantDepVowel = aGss.iParam.iOutput[1].iCode;
if (ThaiGlyph::IsThaiDepVowelAbove(wantDepVowel)
|| ThaiGlyph::IsThaiDepVowelAbovePUA(wantDepVowel)
|| wantDepVowel == ThaiGlyph::KNikhahit
|| wantDepVowel == ThaiGlyph::KNikhahitPua)
return EFalse;
return ETrue;
}
/**
ThaiGlyphPUASubstitution rule method which checks the context for tall
consonant with either a dependent vowel above or nikhahit.
@param aGss
Container object holds the glyph selection context for the method.
@return TBool
ETrue when the rule context is satisfied, EFalse if not.
*/
TBool RuleTallConsonantVowelAbove(const TGlyphSelectionState& aGss)
{
if ((aGss.iParam.iOutputGlyphs == 2) &&
ThaiGlyph::IsThaiTallConsonant(aGss.iParam.iOutput[0].iCode) &&
(ThaiGlyph::IsThaiDepVowelAbovePUA(aGss.iParam.iOutput[1].iCode))
|| aGss.iParam.iOutput[1].iCode == ThaiGlyph::KNikhahitPua)
return ETrue;
else
return EFalse;
}
/**
ThaiGlyphPUASubstitution rule method which checks the context for
consonant with joined descender preceding char.
@param aGss
Container object holds the glyph selection context for the method.
@return TBool
ETrue when the rule context is satisfied, EFalse if not.
*/
TBool RuleConsonantWithJointDescender(const TGlyphSelectionState& aGss)
{
if ((aGss.iParam.iOutputGlyphs == 1) &&
ThaiGlyph::IsThaiConsonantWithJointDescender(aGss.iParam.iOutput[0].iCode))
return ETrue;
else
return EFalse;
}
const PUASubstTableEntry RuleTable[] = {
/**
This data member of the ThaiGlyphPUASubstitution class holds rules
on when a given PUA glyph should be substituted for the original
0x0Exx glyph. Table lookup returns the first match found from the
start of the table, therefore duplicate match situations must be
avoided in the rule set logic.
*/
/* iOrigGlyph, iPUAGlyph, iRuleFunc */
// Substitutions for a tone or sign mark above a short consonant
{ 0x0E48, 0xF70A, RuleShortConsonant },
{ 0x0E49, 0xF70B, RuleShortConsonant },
{ 0x0E4A, 0xF70C, RuleShortConsonant },
{ 0x0E4B, 0xF70D, RuleShortConsonant },
{ 0x0E4C, 0xF70E, RuleShortConsonant },
// Substitutions for a vowel or sign mark above a tall consonant
{ 0x0E34, 0xF701, RuleTallConsonant },
{ 0x0E35, 0xF702, RuleTallConsonant },
{ 0x0E36, 0xF703, RuleTallConsonant },
{ 0x0E37, 0xF704, RuleTallConsonant },
{ 0x0E31, 0xF710, RuleTallConsonant },
{ 0x0E4D, 0xF711, RuleTallConsonant },
{ 0x0E47, 0xF712, RuleTallConsonant },
// Substitutions for a tone or sign mark above a tall consonant
{ 0x0E48, 0xF705, RuleTallConsonantNoVowelAbove },
{ 0x0E49, 0xF706, RuleTallConsonantNoVowelAbove },
{ 0x0E4A, 0xF707, RuleTallConsonantNoVowelAbove },
{ 0x0E4B, 0xF708, RuleTallConsonantNoVowelAbove },
{ 0x0E4C, 0xF709, RuleTallConsonantNoVowelAbove },
// Substitutions for a tone or sign mark above a vowel which is
// above a tall consonant
{ 0x0E48, 0xF713, RuleTallConsonantVowelAbove },
{ 0x0E49, 0xF714, RuleTallConsonantVowelAbove },
{ 0x0E4A, 0xF715, RuleTallConsonantVowelAbove },
{ 0x0E4B, 0xF716, RuleTallConsonantVowelAbove },
{ 0x0E4C, 0xF717, RuleTallConsonantVowelAbove },
// Substitutions for a vowel or sign mark below a consonant with a
// joined descender
{ 0x0E38, 0xF718, RuleConsonantWithJointDescender },
{ 0x0E39, 0xF719, RuleConsonantWithJointDescender },
{ 0x0E3A, 0xF71A, RuleConsonantWithJointDescender },
{ 0, 0, 0}
// Size of table at last measurement was 312 bytes. 27/6/2003
};
/**
This is the lookup method to determine if the current character being
processed needs to be substituted for a glyph in the PUA area given the
supplied context. It scans the rule table and returns when it finds it's
first match. Therefore duplicate match situations must be avoided in
the rule set logic.
@param aCode
On input it is the character to lookup, on exit it is either unchanged
or a code in the PUA 0xF700..0xF71A.
@param aGss
Container object holds the glyph selection context for the method.
@return TBool
ETrue when a match is found and aCode has changed, EFalse otherwise.
*/
TBool Lookup(TUint& aCode, const TGlyphSelectionState& aGss)
{
const PUASubstTableEntry* tablePtr = RuleTable;
while (tablePtr->iOrigGlyph)
{
if ((aCode == tablePtr->iOrigGlyph) && tablePtr->iRuleFunc(aGss))
{
aCode = tablePtr->iPUAGlyph;
return ETrue; // Rule match, substitute glyph code
}
tablePtr++;
}
return EFalse; // No match in table
}
}
//
//
// GlyphSelector_Thai Class definition
//
//
/**
This is the default glyph processing method for the Thai characters in the
range 0x0E00..0x0E7F and is invoked from the Glyph selection algorithm in
CFont::GetCharacterPosition() method. It is capable of processing base
Thai characters as well as Thai combining vowels, signs a tone marks.
@param aGss
Container object holds the input/output parameters of the method.
@return TBool
ETrue when glyph cluster updated successfully, EFalse on error condition.
@see
The method GlyphSelector_Thai::Process() also handles it for other cases.
*/
TBool GlyphSelector_Thai::Process(TGlyphSelectionState& aGss, RShapeInfo&)
{
// Get the Unicode character codes we need to process the current
// glyph and increment the iterator onto th next character.
TUint prevCode = (aGss.iText.LengthToStart() > 0) ? aGss.iText.Get(-1) : 0xFFFF;
TUint code = aGss.iText.GetThenNext(); // Inc to next char
TUint nextCode = !aGss.iText.AtEnd() ? aGss.iText.Get(0) : 0xFFFF;
// Is it a Thai base char or a mark (combining) char?
if ((aGss.iCats & 0xF0) == TChar::EMarkGroup)
{
// Thai character is combining mark but first check to see if it
// follows a Thai base character before processing it.
if ((aGss.iParam.iOutputGlyphs > 0) &&
!ThaiGlyph::IsThaiGlyph(prevCode))
{
(void) aGss.iText.Prev();
aGss.iClusterState = TGlyphSelectionState::EGClusterComplete;
return ETrue;
}
// Missing base glyph? Insert a dotted circle glyph if true.
if (aGss.iParam.iOutputGlyphs == 0)
{
if (!aGss.AppendGlyphToCluster(KUnicodeDottedCircle))
return EFalse;
aGss.iParam.iPen += aGss.iAdvance;
}
// Test if SARA AM follows this current Thai mark, since it is
// a SPECIAL CASE. If present we need NIKHAHIT glyph before this
// current Thai mark char.
if (nextCode == ThaiGlyph::KSaraAm &&
(aGss.iParam.iOutputGlyphs == 1) && ThaiGlyph::IsThaiToneMark(code))
{
TUint nikhahit = ThaiGlyph::KNikhahit;
// Check and do PUA glyph substitution on Nikhahit
ThaiGlyphPUASubstitution::Lookup(nikhahit, aGss);
if (!aGss.AppendGlyphToCluster(nikhahit))
return EFalse;
// Check and do PUA glyph substitution on combining mark
ThaiGlyphPUASubstitution::Lookup(code, aGss);
// Append the curernt Thai Mark to the output stack of glyphs.
if (!aGss.AppendGlyphToCluster(code))
return EFalse;
// We now need to add SARA AA glyph after the current Thai mark char.
aGss.iAdvance.iWidth = aGss.iAdvance.iHeight = 0;
if (!aGss.AppendGlyphToCluster(ThaiGlyph::KSaraAa))
return EFalse;
// Skip the following SARA AM character since we've added
// its glyphs to it's previous character's glyph cluster.
// As we've added a base char to the end of the glyph cluster
// make sure the pen is moved on by the caller.
(void) aGss.iText.Next();
aGss.iPen = TGlyphSelectionState::EPenAdvance_Yes;
}
else
{
// Check and do PUA glyph substitution on combining mark
ThaiGlyphPUASubstitution::Lookup(code, aGss);
// Append the curernt Thai Mark to the output stack of glyphs.
if (!aGss.AppendGlyphToCluster(code))
return EFalse;
aGss.iPen = TGlyphSelectionState::EPenAdvance_No;
}
}
else
{
// Thai character is an independent consonant, digit or sign
// Handle disjoint descender consonants followed by below vowel.
// In these two cases we substitute consonant with PUA
// consonant that the descender removed. Check code not last one.
if (code == ThaiGlyph::KYoYing && nextCode != 0xffff &&
ThaiGlyph::IsThaiDepVowelBelow(nextCode))
code = ThaiGlyph::KYoYingPua;
else if (code == ThaiGlyph::KThoThan && nextCode != 0xffff &&
ThaiGlyph::IsThaiDepVowelBelow(nextCode))
code = ThaiGlyph::KThoThanPua;
// Append the glyph details for the Thai character onto the output
// stack of glyphs.
if (!aGss.AppendGlyphToCluster(code))
return EFalse;
// Make sure the caller advances the pen for a base char!
aGss.iPen = TGlyphSelectionState::EPenAdvance_Yes;
}
// Lookup in rule table to determine if the current glyph and cluster is
// now complete?
if (ThaiCharRules::LookupWtt2Rule(aGss.iCodePt, nextCode) == ThaiCharRules::EComposite)
aGss.iClusterState = TGlyphSelectionState::EGClusterNotComplete;
else
aGss.iClusterState = TGlyphSelectionState::EGClusterComplete;
return ETrue;
}
//
//
// GlyphSelector_ThaiSaraAm Class definition
//
//
/**
This is the glyph processing method for the Thai SARA AM (U+0E33) character
which is handled as a special case since it is decomposed into two glyphs
- the combining NIKHAHIT mark & SARA AA following vowel in some cases.
It is invoked from the Glyph selection algorithm in
CFont::GetCharacterPosition() method for all cases where SARA AM is not
following a tone mark and thus be a glyph cluster of its own.
@param aGss
Container object holds the input/output parameters of the method.
@return TBool
ETrue when glyph cluster updated successfully, EFalse on error condition.
@see
The method GlyphSelector_Thai::Process() also handles it for other cases.
*/
TBool GlyphSelector_ThaiSaraAm::Process(TGlyphSelectionState& aGss, RShapeInfo&)
{
if (aGss.iCodePt != ThaiGlyph::KSaraAm) //could have got here via
{ //FindLocalisedProcessFunc in font.cpp
RShapeInfo dummy;
return GlyphSelector_Thai::Process(aGss, dummy);
}
// Pen advance accumulator local variable
TSize compoundAdvance;
if (aGss.iText.LengthToStart() == 0)
{
// If at the start of a line then render it with a preceding
// dotted circle as this is invalid positioning for SARA AM.
if (!aGss.AppendGlyphToCluster(KUnicodeDottedCircle))
return EFalse;
aGss.iParam.iPen += aGss.iAdvance;
aGss.iAdvance.iWidth = aGss.iAdvance.iHeight = 0;
if (!aGss.AppendGlyphToCluster(ThaiGlyph::KSaraAm))
return EFalse;
compoundAdvance += aGss.iAdvance;
}
else
{
// Normal condition - text iterator now some way into the text line
// being processed.
TUint prevChar = aGss.iText.Get(-1);
if (ThaiGlyph::IsThaiShortConsonant(prevChar))
{
// SARA AM is following normal height consonant so we can output
// non-decomposed SARA AM glyph.
if (!aGss.AppendGlyphToCluster(ThaiGlyph::KSaraAm))
return EFalse;
compoundAdvance = aGss.iAdvance;
}
else if (ThaiGlyph::IsThaiTallConsonant(prevChar))
{
// SARA AM is following tall consonant so we output decomposed
// version of SARA AM but with NIKHAHIT taken from the PUA.
if (!aGss.AppendGlyphToCluster(ThaiGlyph::KNikhahitPua))
return EFalse;
compoundAdvance = aGss.iAdvance;
aGss.iAdvance.iWidth = aGss.iAdvance.iHeight = 0;
if (!aGss.AppendGlyphToCluster(ThaiGlyph::KSaraAa))
return EFalse;
compoundAdvance += aGss.iAdvance;
}
else
{
// SARA AM is a following vowel but is not following a valid
// consonant char and so default is to render with dotted circle.
if (!aGss.AppendGlyphToCluster(KUnicodeDottedCircle))
return EFalse;
aGss.iParam.iPen += aGss.iAdvance;
aGss.iAdvance.iWidth = aGss.iAdvance.iHeight = 0;
if (!aGss.AppendGlyphToCluster(ThaiGlyph::KSaraAm))
return EFalse;
compoundAdvance += aGss.iAdvance;
}
}
// Update output parameters resulting from above processing.
// Move text iterator onto next character to process.
aGss.iText.Next();
// Advance pen just for the SARA AA char as advance for dotted
// circle is done above.
aGss.iAdvance = compoundAdvance;
aGss.iPen = TGlyphSelectionState::EPenAdvance_Yes;
if (!aGss.iText.AtEnd() &&
(ThaiCharRules::LookupWtt2Rule(aGss.iCodePt, aGss.iText.Get()) ==
ThaiCharRules::EComposite))
aGss.iClusterState = TGlyphSelectionState::EGClusterNotComplete;
else
aGss.iClusterState = TGlyphSelectionState::EGClusterComplete;
return ETrue;
}