|
|
/*
* Uniscribe interface (& related classes) class implementation * * File: uspi.cpp * Create: Jan 10, 1998 * Author: Worachai Chaoweeraprasit (wchao) * * Copyright (c) 1998, Microsoft Corporation. All rights reserved. */
#include "_common.h"
#include "_font.h"
#include "_frunptr.h"
#include "_select.h"
#include "_measure.h"
#include "_uspi.h"
CUniscribe* g_pusp = NULL; int g_cMaxScript = 0x100;
// initial dummy script properties (= SCRIPT_UNDEFINED)
static const SCRIPT_PROPERTIES g_propUndef = { LANG_NEUTRAL, FALSE, FALSE, FALSE, FALSE, 0 }; static const SCRIPT_PROPERTIES* g_pPropUndef[1] = { &g_propUndef };
CUniscribe::CUniscribe () { // Initialize digit substitution info
ApplyDigitSubstitution(W32->GetDigitSubstitutionMode());
// Get maximum number of scripts supported
ScriptGetProperties(NULL, &g_cMaxScript); }
// Test the OS if it does any complex script.
// REVIEW (keithcu) What if it only supports indic, but not the other ones?
BOOL IsSupportedOS() { BOOL fSupport = !OnWin95FE(); int rguCodePage[] = {1255, 1256, 874}; BYTE rgbch[] = {0xe0, 0xd3, 0xa1}; WCHAR rgwch[] = {0x05d0, 0x0633, 0x0e01}; WCHAR wch; int i = 0;
if (fSupport) { for (;i < 3; i++) { if (MBTWC(rguCodePage[i], 0, (LPCSTR)&rgbch[i], 1, (LPWSTR)&wch, 1, NULL) > 0 && wch == rgwch[i]) break; // support either Arabic, Hebrew or Thai
} } return fSupport && i < 3; }
// Prepare information for digit substitution
// return: Native digit script (shapine engine) ID.
//
WORD CUniscribe::ApplyDigitSubstitution(BYTE bDigitSubstMode) { _wesNationalDigit = 0;
// Remember national digits script ID if substitution mode is not None
if (bDigitSubstMode != DIGITS_NOTIMPL && bDigitSubstMode != DIGITS_NONE) { WCHAR chZero = 0x0030; int cItems; SCRIPT_ITEM si[2]; SCRIPT_CONTROL sc = {0}; SCRIPT_STATE ss = {0};
// force national digit mode
sc.uDefaultLanguage = GetNationalDigitLanguage(GetThreadLocale()); ss.fDigitSubstitute = TRUE; sc.fContextDigits = FALSE;
if (SUCCEEDED(ScriptItemize(&chZero, 1, 2, &sc, &ss, (SCRIPT_ITEM*)&si, (int*)&cItems))) _wesNationalDigit = si[0].a.eScript; } return _wesNationalDigit; }
// Some locales may have its own traditional (native) digit and national standard digit
// recognised by a standard body and adopted by NLSAPI. The example is that Nepali(India)
// has its own digit but the India standard uses Hindi digit as the national digit.
//
DWORD CUniscribe::GetNationalDigitLanguage(LCID lcid) { DWORD dwDigitLang = PRIMARYLANGID(LANGIDFROMLCID(lcid));
if (W32->OnWinNT5()) { WCHAR rgwstrDigit[20];
if (GetLocaleInfoW(lcid, LOCALE_SNATIVEDIGITS, rgwstrDigit, ARRAY_SIZE(rgwstrDigit))) { // Steal this from Uniscribe (build 0231)
switch (rgwstrDigit[1]) { case 0x0661: dwDigitLang = LANG_ARABIC; break; case 0x06F1: dwDigitLang = LANG_FARSI; break; case 0x0e51: dwDigitLang = LANG_THAI; break; case 0x0967: dwDigitLang = LANG_HINDI; break; case 0x09e7: dwDigitLang = LANG_BENGALI; break; case 0x0a67: dwDigitLang = LANG_PUNJABI; break; case 0x0ae7: dwDigitLang = LANG_GUJARATI; break; case 0x0b67: dwDigitLang = LANG_ORIYA; break; case 0x0be7: dwDigitLang = LANG_TAMIL; break; case 0x0c67: dwDigitLang = LANG_TELUGU; break; case 0x0ce7: dwDigitLang = LANG_KANNADA; break; case 0x0d67: dwDigitLang = LANG_MALAYALAM; break; case 0x0f21: dwDigitLang = LANG_TIBETAN; break; case 0x0ed1: dwDigitLang = LANG_LAO; break; } } }
return dwDigitLang; }
CUniscribe::~CUniscribe () { if (_pFSM) { delete _pFSM; } }
/***** High level services *****/
// Tokenize string and run Unicode Bidi algorithm if requested.
// return : =<0 - error
// >0 - number of complex script tokens
//
int CUniscribe::ItemizeString ( USP_CLIENT* pc, // in: Working structure
WORD uInitLevel, // in: Initial Bidi level
int* pcItems, // out: Count of items generated
WCHAR* pwchString, // in: Input string
int cch, // in: Number of character to itemize
BOOL fUnicodeBiDi, // in: TRUE - Use UnicodeBidi
WORD wLangId) // in: (optional) Dominant language preference
{ Assert (pc && pc->si && pcItems && pwchString && cch > 0 && cch <= pc->si->cchString);
USP_CLIENT_SI* pc_si = pc->si; SCRIPT_ITEM* psi = pc_si->psi; SCRIPT_CONTROL sc = {0}; SCRIPT_STATE ss = {0}; SCRIPT_CONTROL* psc; SCRIPT_STATE* pss; HRESULT hr; int cItems = 0;
if (fUnicodeBiDi) { psc = ≻ pss = &ss;
if (wLangId == LANG_NEUTRAL) wLangId = PRIMARYLANGID(LANGIDFROMLCID(GetThreadLocale()));
// (preitemize:) set up initial state
psc->uDefaultLanguage = wLangId; // For Arabic Office's compatibility.
// We enable fArabicNumContext if the dominant language is Arabic.
//
if (psc->uDefaultLanguage == LANG_ARABIC) pss->fArabicNumContext = uInitLevel & 1;
pss->uBidiLevel = uInitLevel; // Leave digit substitution to None since we do it ourself.
// pss->fDigitSubstitute = FALSE;
// psc->fContextDigits = FALSE;
} else { psc = NULL; pss = NULL; }
// begin real work
hr = ScriptItemize(pwchString, cch, cch+1, psc, pss, psi, (int*)&cItems);
return SUCCEEDED(hr) ? *pcItems = cItems : 0; }
// Produce a shaped string (glyph array), taking care of font association and measurer's CF update
//
// Success can require 3 calls to Shape():
// 1. Returns E_PENDING (script cache doesn't contain the glyphing information)
// 2. Return USP_E_SCRIPT_NOT_IN_FONT --the HFONT doesn't contain the script needed to do the glyphing
// 3. Hopefully success, but may return again if the fallback font doesn't exist, but we quit anyway.
int CUniscribe::ShapeString ( PLSRUN plsrun, // in: The first run to be shaped
SCRIPT_ANALYSIS* psa, // in: Analysis of the run to be shaped
CMeasurer* pme, // in: Measurer points to start cp of the run
const WCHAR* pwch, // in: String to be shaped
int cch, // in: Count of chars
WORD*& pwgi, // out: Reference to glyph indices array
WORD* pwlc, // out: Logical cluster array
SCRIPT_VISATTR*& psva) // out: Reference to glyph's attribute array
{ AssertSz (plsrun && psa && pme && pwch, "ShapeString failed: Invalid params");
HRESULT hr = S_OK; HRESULT hrLastError = S_OK; HDC hdc = NULL; HFONT hOrgFont = NULL; int cGlyphs; int cchAdd = 0; CCcs *pccsSave = pme->Check_pccs(); int nAttempt = 8; // Maximum attempt to realloc glyph buffer to shape a string
// make sure that we have proper font cache ready to use
if (!pme->_pccs) return 0; if (psa->fNoGlyphIndex) // If no glyph processing, hdc must be around.
hdc = PrepareShapeDC(pme, E_PENDING, hOrgFont);
// prepare glyph buffer
if (!CacheAllocGlyphBuffers(cch, cGlyphs, pwgi, psva)) return 0;
do { hr = ScriptShape(hdc, &pme->_pccs->_sc, pwch, cch, cGlyphs, psa, pwgi, pwlc, psva, &cGlyphs);
if (SUCCEEDED(hr)) break;
// Error handling...
switch (hr) { case E_PENDING: case USP_E_SCRIPT_NOT_IN_FONT:
if (hr == hrLastError) nAttempt = 0; // We encounter the same error twice.
else { hdc = PrepareShapeDC(pme, hr, hOrgFont); hrLastError = hr; } break;
case E_OUTOFMEMORY:
// (#6773)Indic shaping engine could produce glyphs more than we could hold.
//
cchAdd += 16; if (CacheAllocGlyphBuffers(cch + cchAdd, cGlyphs, pwgi, psva)) { nAttempt--; break; } default: nAttempt = 0; //AssertSz(FALSE, "Shaping fails with invalid error or we run out of memory.");
break; }
} while (nAttempt > 0);
// restore hdc's original font
if (hdc && hOrgFont) SelectObject(hdc, hOrgFont);
if (pme->_pccs != pccsSave) plsrun->SetFallback(SUCCEEDED(hr));
return SUCCEEDED(hr) ? cGlyphs : 0; }
// Place a string and take care of font association and measurer's CF update
//
// This is called right after ShapeString.
int CUniscribe::PlaceString( PLSRUN plsrun, // in: The first run to be shaped
SCRIPT_ANALYSIS* psa, // in: Analysis of the run to be shaped
CMeasurer* pme, // in: Measurer points to start cp of the run
const WORD* pcwgi, // in: Glyph indices array
int cgi, // in: Count of input glyphs
const SCRIPT_VISATTR* psva, // in: Glyph's attribute array
int* pgdx, // out: Glyph's advanced width array
GOFFSET* pgduv, // out: Glyph's offset array
ABC* pABC) // out: Run's dimension
{ AssertSz (plsrun && psa && pme && pcwgi, "PlaceString failed: Invalid params");
HRESULT hr = S_OK; HRESULT hrLastError = S_OK; HDC hdc = NULL; HFONT hOrgFont = NULL; int nAttempt = 1;
pme->Check_pccs(); pme->ApplyFontCache(plsrun->IsFallback());
// make sure that we have proper font cache ready to use
if (!pme->_pccs) return 0;
if (psa->fNoGlyphIndex) // If no glyph processing, hdc must be around.
hdc = PrepareShapeDC(pme, E_PENDING, hOrgFont);
do { hr = ScriptPlace(hdc, &pme->_pccs->_sc, pcwgi, cgi, psva, psa, pgdx, pgduv, pABC);
if (SUCCEEDED(hr)) break;
// Error handling...
switch (hr) { case E_PENDING:
if (hr == hrLastError) nAttempt = 0; // We encounter the same error twice.
else { hdc = PrepareShapeDC(pme, hr, hOrgFont); hrLastError = hr; } break;
default: nAttempt = 0; //AssertSz(FALSE, "Placing fails with invalid error.");
break; }
} while (nAttempt > 0);
// restore hdc's original font
if (hdc && hOrgFont) SelectObject(hdc, hOrgFont);
return SUCCEEDED(hr) ? cgi : 0; }
// Placing given string results in logical width array,
// the result array would be used to record WMF metafile.
//
int CUniscribe::PlaceMetafileString ( PLSRUN plsrun, // in: The first run to be shaped
CMeasurer* pme, // in: Measurer points to start cp of the run
const WCHAR* pwch, // in: Input codepoint string
int cch, // in: Character count
PINT* ppiDx) // out: Pointer to logical widths array
{ AssertSz (pme && pwch && ppiDx, "PlaceMetafileString failed: Invalid params");
if (W32->OnWinNT4() || W32->OnWin9xThai()) { // MET NT40 has bug in lpdx justification so i doesnt playback the lpdx very nicely.
// Thai Win9x simply cannot handle fancy lpdx values generated by Uniscribe.
// We workaround both cases here by metafiling no lpdx and let the system reconstructs
// it from scratch during playback time.
// =FUTURE= If we do line justification. We need more sophisticated work here
// basically to reconstruct the OS preferred type of lpdx.
//
*ppiDx = NULL; return cch; }
HRESULT hr = S_OK; PUSP_CLIENT pc = NULL; int* piDx; // result logical width array
int cgi = 0; BYTE pbBufIn[MAX_CLIENT_BUF]; SCRIPT_ANALYSIS sa = plsrun->_a; BOOL fVisualGlyphDx = sa.fRTL && W32->OnWin9x() && W32->OnBiDiOS();
// Get static buffer for logical widths array
if (!(piDx = GetWidthBuffer(cch))) return 0;
CreateClientStruc(pbBufIn, MAX_CLIENT_BUF, &pc, cch, cli_ShapePlace); if (!pc) return 0;
PUSP_CLIENT_SSP pcssp = pc->ssp; if (fVisualGlyphDx) sa.fLogicalOrder = FALSE; // shaping result in visual order
// Shape string
if (cgi = ShapeString(plsrun, &sa, pme, pwch, (int)cch, pcssp->pwgi, pcssp->pcluster, pcssp->psva)) { // then place it...
if (cgi == PlaceString(plsrun, &sa, pme, pcssp->pwgi, cgi, pcssp->psva, pcssp->pidx, pcssp->pgoffset, NULL)) { if (fVisualGlyphDx) { // Workaround BiDi Win9x's lpdx handling
// It assumes ExtTextOut's dx array is glyph width in visual order
Assert (cgi <= cch); // glyph count never exceeds character count in BiDi
CopyMemory (piDx, pcssp->pidx, min(cgi, cch)*sizeof(int)); } else { // Map visual glyph widths to logical widths
hr = ScriptGetLogicalWidths(&sa, cch, cgi, pcssp->pidx, pcssp->pcluster, pcssp->psva, piDx); } } }
// result
*ppiDx = piDx;
if (pc && pbBufIn != (BYTE*)pc) FreePv(pc);
return SUCCEEDED(hr) ? cgi : 0; }
/***** Helper functions *****/
// Retrieve the BidiLevel FSM
const CBiDiFSM* CUniscribe::GetFSM () { if (!_pFSM) { _pFSM = new CBiDiFSM(this); if (_pFSM && !_pFSM->Init()) { delete _pFSM; } } return _pFSM; }
// Prepare the shapeable font ready to dc for a given script
//
// USP_E_SCRIPT_NOT_IN_FONT - complex scripts font association
// E_PENDING - prepare dc with current font selected
//
HDC CUniscribe::PrepareShapeDC ( CMeasurer* pme, // in: Measurer points to start cp of the run
HRESULT hrReq, // in: Error code to react
HFONT& hOrgFont) // in/out: Original font of the shape DC
{ Assert (pme); HDC hdc = NULL; HFONT hOldFont;
switch (hrReq) { case USP_E_SCRIPT_NOT_IN_FONT: { pme->ApplyFontCache(fTrue); #ifdef DEBUG
if (pme->_pccs) Tracef(TRCSEVWARN, "USP_E_SCRIPT_NOT_IN_FONT: charset %d applied", pme->_pccs->_bCharSet); #endif
} default: if (pme->_pccs) { hdc = pme->_pccs->_hdc; hOldFont = (HFONT)SelectObject(hdc, pme->_pccs->_hfont);
if (!hOrgFont) hOrgFont = hOldFont; } }
return hdc; }
const SCRIPT_PROPERTIES* CUniscribe::GeteProp (WORD eScript) { if (!_ppProp) { if (!SUCCEEDED(ScriptGetProperties(&_ppProp, NULL)) || !_ppProp) _ppProp = g_pPropUndef; } if (_ppProp == g_pPropUndef || eScript >= (WORD)g_cMaxScript) eScript = 0;
return _ppProp[eScript]; }
// Figure proper charset to use for complex script.
// The resulted charset can be either actual or virtual (internal) GDI charset used by given script
BOOL CUniscribe::GetComplexCharSet( const SCRIPT_PROPERTIES* psp, // Uniscribe script's properties
BYTE bCharSetDefault, // -1 format's charset
BYTE& bCharSetOut) // out: Charset to use
{ Assert(psp);
BYTE bCharSet = !psp->fCDM ? psp->bCharSet : GetCDMCharSet(bCharSetDefault); BOOL fr = psp->fComplex && !psp->fControl;
if (fr) { bCharSetOut = IN_RANGE(ANSI_CHARSET, bCharSet, DEFAULT_CHARSET) ? GetCharSet(ConvertLanguageIDtoCodePage(psp->langid), NULL) : bCharSet; } return fr; }
// Figure out the charset to use for CDM run
//
BYTE CUniscribe::GetCDMCharSet(BYTE bCharSetDefault) { if (!_bCharSetCDM) {
_bCharSetCDM = (bCharSetDefault == VIETNAMESE_CHARSET || W32->GetPreferredKbd(VIET_INDEX) || GetCharSet(GetLocaleCodePage(), NULL) == VIETNAMESE_CHARSET || GetCharSet(GetACP(), NULL) == VIETNAMESE_CHARSET) ? VIETNAMESE_CHARSET : DEFAULT_CHARSET; } return _bCharSetCDM; }
BYTE CUniscribe::GetRtlCharSet(CTxtEdit* ped) { if (!_bCharSetRtl) { // First, try default charset
DWORD dwCharFlags; BYTE bCharSet = ped->GetCharFormat(-1)->_bCharSet;
if (!IN_RANGE(HEBREW_CHARSET, bCharSet, ARABIC_CHARSET)) { // then the system charset
bCharSet = GetCharSet(GetACP(), NULL); if (!IN_RANGE(HEBREW_CHARSET, bCharSet, ARABIC_CHARSET)) { // then the content
dwCharFlags = ped->GetCharFlags() & (fARABIC | fHEBREW);
if (dwCharFlags == fARABIC) bCharSet = ARABIC_CHARSET; else if(dwCharFlags == fHEBREW) bCharSet = HEBREW_CHARSET; else { // and last chance with the first found loaded Bidi kbd
if (W32->GetPreferredKbd(HEBREW_INDEX)) bCharSet = HEBREW_CHARSET; else // Even if we cant find Arabic, we have to assume it here.
bCharSet = ARABIC_CHARSET; } } } Assert(IsBiDiCharSet(bCharSet)); _bCharSetRtl = bCharSet; } return _bCharSetRtl; }
// Substitute digit shaper in plsrun if needed
//
void CUniscribe::SubstituteDigitShaper ( PLSRUN plsrun, CMeasurer* pme) { Assert(plsrun && pme);
CTxtEdit* ped = pme->GetPed(); WORD wScript;
if (GeteProp(plsrun->_a.eScript)->fNumeric) { wScript = plsrun->_pCF->_wScript; // reset it before
switch (W32->GetDigitSubstitutionMode()) { case DIGITS_CTX: { if (ped->IsRich()) { // Context mode simply means the charset of the kbd for richtext.
if (!IsBiDiCharSet(ped->GetCharFormat(pme->_rpCF.GetFormat())->_bCharSet)) break; } else { // Digit follows directionality of preceding run for plain text
CFormatRunPtr rp(pme->_rpCF); Assert(rp.IsValid());
if (rp.PrevRun()) { if (!IsBiDiCharSet(ped->GetCharFormat(rp.GetFormat())->_bCharSet)) break; } else { // No preceding run, looking for the paragraph direction
if (!pme->Get_pPF()->IsRtlPara()) break; } } // otherwise, fall thru...
} case DIGITS_NATIONAL: wScript = _wesNationalDigit; default: break; }
// Update all linked runs
while (plsrun) { plsrun->_a.eScript = wScript; // assign proper shaping engine to digits
plsrun = plsrun->_pNext; } } }
/***** Uniscribe entry point *****/
// memory allocator
//
BOOL CUniscribe::CreateClientStruc ( BYTE* pbBufIn, LONG cbBufIn, PUSP_CLIENT* ppc, LONG cchString, DWORD dwMask) { Assert(ppc && pbBufIn);
if (!ppc) return FALSE;
*ppc = NULL;
if (cchString == 0) cchString = 1; // simplify caller's logic
LONG i; LONG cbSize; PBYTE pbBlock;
// ScriptItemize's
//
PVOID pvString; PVOID pvsi;
// ScriptBreak's
//
PVOID pvsla;
// ScriptShape & Place's
//
PVOID pvwgi; PVOID pvsva; PVOID pvcluster; PVOID pvidx; PVOID pvgoffset;
// subtable ptrs
//
PUSP_CLIENT_SI pc_si; PUSP_CLIENT_SB pc_sb; PUSP_CLIENT_SSP pc_ssp;
#define RQ_COUNT 12
BUF_REQ brq[RQ_COUNT] = { // table and subtable blocks
//
{ sizeof(USP_CLIENT), 1, (void**)ppc}, { sizeof(USP_CLIENT_SI), dwMask & cli_Itemize ? 1 : 0, (void**)&pc_si}, { sizeof(USP_CLIENT_SB), dwMask & cli_Break ? 1 : 0, (void**)&pc_sb}, { sizeof(USP_CLIENT_SSP), dwMask & cli_ShapePlace ? 1 : 0, (void**)&pc_ssp},
// data blocks
//
{ sizeof(WCHAR), dwMask & cli_string ? cchString + 1 : 0, &pvString}, { sizeof(SCRIPT_ITEM), dwMask & cli_psi ? cchString + 1 : 0, &pvsi}, { sizeof(SCRIPT_LOGATTR), dwMask & cli_psla ? cchString + 1 : 0, &pvsla}, { sizeof(WORD), dwMask & cli_pwgi ? GLYPH_COUNT(cchString+1) : 0, &pvwgi}, { sizeof(SCRIPT_VISATTR), dwMask & cli_psva ? GLYPH_COUNT(cchString+1) : 0, &pvsva}, { sizeof(WORD), dwMask & cli_pcluster ? cchString + 1 : 0, &pvcluster}, { sizeof(int), dwMask & cli_pidx ? GLYPH_COUNT(cchString+1) : 0, &pvidx}, { sizeof(GOFFSET), dwMask & cli_pgoffset ? GLYPH_COUNT(cchString+1) : 0, &pvgoffset}, };
// count total buffer size in byte (WORD aligned)
//
for (i=0, cbSize=0; i < RQ_COUNT; i++) { cbSize += ALIGN(brq[i].size * brq[i].c); }
// allocate the whole buffer at once
//
if (cbSize > cbBufIn) { pbBlock = (PBYTE)PvAlloc(cbSize, 0); } else { pbBlock = pbBufIn; }
if (!pbBlock) { //
// memory management failed!
//
TRACEERRORSZ("Allocation failed in CreateClientStruc!\n"); *ppc = NULL; return FALSE; }
// clear the main table
ZeroMemory (pbBlock, sizeof(USP_CLIENT));
// assign ptrs in buffer request structure
//
for (i=0; i < RQ_COUNT; i++) { if (brq[i].c > 0) { *brq[i].ppv = pbBlock; pbBlock += ALIGN(brq[i].size * brq[i].c); } else { *brq[i].ppv = NULL; } }
Assert(((PBYTE)(*ppc)+cbSize == pbBlock));
// fill in data block ptrs in subtable
//
if (pc_si) { pc_si->pwchString = (WCHAR*) pvString; pc_si->cchString = cchString; pc_si->psi = (SCRIPT_ITEM*) pvsi; }
if (pc_sb) { pc_sb->psla = (SCRIPT_LOGATTR*) pvsla; }
if (pc_ssp) { pc_ssp->pwgi = (WORD*) pvwgi; pc_ssp->psva = (SCRIPT_VISATTR*) pvsva; pc_ssp->pcluster = (WORD*) pvcluster; pc_ssp->pidx = (int*) pvidx; pc_ssp->pgoffset = (GOFFSET*) pvgoffset; }
// fill in subtable ptrs in header table
//
(*ppc)->si = (PUSP_CLIENT_SI) pc_si; (*ppc)->sb = (PUSP_CLIENT_SB) pc_sb; (*ppc)->ssp = (PUSP_CLIENT_SSP) pc_ssp;
return TRUE; }
/////// CBidiFSM class implementation
//
// Create: Worachai Chaoweeraprasit(wchao), Jan 29, 1998
//
CBiDiFSM::~CBiDiFSM () { FreePv(_pStart); }
INPUT_CLASS CBiDiFSM::InputClass ( const CCharFormat* pcCF, CTxtPtr* ptp, LONG cchRun) const { if (!_pusp->IsValid() || !pcCF || pcCF->_wScript == SCRIPT_WHITE) return chGround;
const SCRIPT_PROPERTIES* psp = _pusp->GeteProp(pcCF->_wScript); BYTE bCharSet = pcCF->_bCharSet;
if (psp->fControl) { if (cchRun == 1) switch (ptp->GetChar()) // single-char run
{ case LTRMARK: return chLTR; // \ltrmark
case RTLMARK: return chRTL; // \rtlmark
} return chGround; }
if (IsSymbolOrOEM(bCharSet) || IsFECharSet(bCharSet) || pcCF->_dwEffects & CFE_RUNISDBCS) return chLTR;
if (psp->fNumeric) // Numeric digits
return IsBiDiCharSet(psp->bCharSet) || IsBiDiCharSet(bCharSet) ? digitRTL : digitLTR;
// RTL if it's RTL script or its format charset is RTL and NOT a simplified script
return IsBiDiCharSet(psp->bCharSet) || pcCF->_wScript && IsBiDiCharSet(bCharSet) ? chRTL : chLTR; }
// The FSM generates run's embedding level based on given base level and puts it
// in CFormatRun. LsFetchRun is the client using this result.
//
#ifdef DEBUG
//#define DEBUG_LEVEL
#endif
#ifdef DEBUG_LEVEL
void DebugLevel (CBiDiFSMCell* pCell) { Tracef(TRCSEVNONE, "%d,", pCell->_level._value); } #else
#define DebugLevel(x)
#endif
HRESULT CBiDiFSM::RunFSM ( CRchTxtPtr* prtp, // in: text pointer to start run
LONG cRuns, // in: number of FSM run
LONG cRunsStart, // in: number of start run
BYTE bBaseLevel) const // in: base level
{ Assert (prtp->_rpCF.IsValid() && cRuns > 0);
CRchTxtPtr rtp(*prtp); const CCharFormat* pCF; LONG cchRun; LONG cRunsAll = cRuns + cRunsStart; CBiDiFSMCell* pCell; USHORT ucState = bBaseLevel ? S_X * NUM_FSM_INPUTS : 0; BOOL fNext = TRUE;
// loop thru FSM
for (; fNext && cRunsAll > 0; cRunsAll--, fNext = !!rtp.Advance(cchRun)) { cchRun = rtp.GetCchLeftRunCF(); pCF = rtp.GetPed()->GetCharFormat(rtp._rpCF.GetFormat()); ucState += InputClass(pCF, &rtp._rpTX, cchRun);
pCell = &_pStart[ucState];
// set level to FSM runs
if (cRunsAll <= cRuns) rtp._rpCF.SetLevel (pCell->_level);
DebugLevel(pCell);
ucState = pCell->_uNext; // next state
}
return S_OK; }
// Construct the BiDi embedding level FSM (FSM details see bidifsm2.html)
// :FSM's size = NUM_FSM_INPUTS * NUM_FSM_STATES * sizeof(CBiDiFSMCell) = 6*5*4 = 120 bytes
//
BOOL CBiDiFSM::Init() { CBiDiFSMCell* pCell; int i;
// Build the Bidi FSM
_nState = NUM_FSM_STATES; _nInput = NUM_FSM_INPUTS;
pCell = (CBiDiFSMCell*)PvAlloc(NUM_FSM_STATES * NUM_FSM_INPUTS * sizeof(CBiDiFSMCell), 0);
if (!pCell) return FALSE; // unable to create FSM!
_pStart = pCell;
CBiDiLevel lvlZero = {0,0}; CBiDiLevel lvlOne = {1,0}; CBiDiLevel lvlTwo = {2,0}; CBiDiLevel lvlTwoStart = {2,1};
// State A(0): LTR char in LTR para
//
for (i=0; i < NUM_FSM_INPUTS; i++, pCell++) { switch (i) { case chLTR: SetFSMCell(pCell, &lvlZero, 0); break; case chRTL: SetFSMCell(pCell, &lvlOne, S_B * NUM_FSM_INPUTS); break; case digitLTR: SetFSMCell(pCell, &lvlZero, 0); break; case digitRTL: SetFSMCell(pCell, &lvlTwo, S_C * NUM_FSM_INPUTS); break; case chGround: SetFSMCell(pCell, &lvlZero, 0); break; } } // State B(1): RTL char in LTR para
//
for (i=0; i < NUM_FSM_INPUTS; i++, pCell++) { switch (i) { case chLTR: SetFSMCell(pCell, &lvlZero, 0); break; case chRTL: SetFSMCell(pCell, &lvlOne, S_B * NUM_FSM_INPUTS); break; case digitLTR: SetFSMCell(pCell, &lvlZero, 0); break; case digitRTL: SetFSMCell(pCell, &lvlTwo, S_C * NUM_FSM_INPUTS); break; case chGround: SetFSMCell(pCell, &lvlZero, 0); break; } } // State C(2): RTL number run in LTR para
//
for (i=0; i < NUM_FSM_INPUTS; i++, pCell++) { switch (i) { case chLTR: SetFSMCell(pCell, &lvlZero, 0); break; case chRTL: SetFSMCell(pCell, &lvlOne, S_B * NUM_FSM_INPUTS); break; case digitLTR: SetFSMCell(pCell, &lvlZero, 0); break; case digitRTL: SetFSMCell(pCell, &lvlTwo, S_C * NUM_FSM_INPUTS); break; case chGround: SetFSMCell(pCell, &lvlZero, 0); break; } } // State X(1): RTL char in RTL para
//
for (i=0; i < NUM_FSM_INPUTS; i++, pCell++) { switch (i) { case chLTR: SetFSMCell(pCell, &lvlTwo, S_Y * NUM_FSM_INPUTS); break; case chRTL: SetFSMCell(pCell, &lvlOne, S_X * NUM_FSM_INPUTS); break; case digitLTR: SetFSMCell(pCell, &lvlTwo, S_Y * NUM_FSM_INPUTS); break; case digitRTL: SetFSMCell(pCell, &lvlTwo, S_Z * NUM_FSM_INPUTS); break; case chGround: SetFSMCell(pCell, &lvlOne, S_X * NUM_FSM_INPUTS); break; } } // State Y(2): LTR char in RTL para
//
for (i=0; i < NUM_FSM_INPUTS; i++, pCell++) { switch (i) { case chLTR: SetFSMCell(pCell, &lvlTwo, S_Y * NUM_FSM_INPUTS); break; case chRTL: SetFSMCell(pCell, &lvlOne, S_X * NUM_FSM_INPUTS); break; case digitLTR: SetFSMCell(pCell, &lvlTwo, S_Y * NUM_FSM_INPUTS); break; case digitRTL: SetFSMCell(pCell, &lvlTwoStart, S_Z * NUM_FSM_INPUTS); break; case chGround: SetFSMCell(pCell, &lvlOne, S_X * NUM_FSM_INPUTS); break; } } // State Z(2): RTL number in RTL para
//
for (i=0; i < NUM_FSM_INPUTS; i++, pCell++) { switch (i) { case chLTR: SetFSMCell(pCell, &lvlTwoStart, S_Y * NUM_FSM_INPUTS); break; case chRTL: SetFSMCell(pCell, &lvlOne, S_X * NUM_FSM_INPUTS); break; case digitLTR: SetFSMCell(pCell, &lvlTwoStart, S_Y * NUM_FSM_INPUTS); break; case digitRTL: SetFSMCell(pCell, &lvlTwo, S_Z * NUM_FSM_INPUTS); break; case chGround: SetFSMCell(pCell, &lvlOne, S_X * NUM_FSM_INPUTS); break; } }
AssertSz(&pCell[-(NUM_FSM_STATES * NUM_FSM_INPUTS)] == _pStart, "Bidi FSM incomplete constructed!");
return TRUE; }
/////// CCallbackBufferBase class implementation
//
void* CBufferBase::GetPtr(int cel) { if (_cElem < cel) { cel += celAdvance;
_p = PvReAlloc(_p, cel * _cbElem); if (!_p) return NULL; ZeroMemory(_p, cel * _cbElem); _cElem = cel; } return _p; }
void CBufferBase::Release() { if (_p) FreePv(_p); }
|