/* * 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, sizeof(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); }