Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1070 lines
26 KiB

  1. /*
  2. * Uniscribe interface (& related classes) class implementation
  3. *
  4. * File: uspi.cpp
  5. * Create: Jan 10, 1998
  6. * Author: Worachai Chaoweeraprasit (wchao)
  7. *
  8. * Copyright (c) 1998, Microsoft Corporation. All rights reserved.
  9. */
  10. #include "_common.h"
  11. #include "_font.h"
  12. #include "_frunptr.h"
  13. #include "_select.h"
  14. #include "_measure.h"
  15. #include "_uspi.h"
  16. CUniscribe* g_pusp = NULL;
  17. int g_cMaxScript = 0x100;
  18. // initial dummy script properties (= SCRIPT_UNDEFINED)
  19. static const SCRIPT_PROPERTIES g_propUndef = { LANG_NEUTRAL, FALSE, FALSE, FALSE, FALSE, 0 };
  20. static const SCRIPT_PROPERTIES* g_pPropUndef[1] = { &g_propUndef };
  21. CUniscribe::CUniscribe ()
  22. {
  23. // Initialize digit substitution info
  24. ApplyDigitSubstitution(W32->GetDigitSubstitutionMode());
  25. // Get maximum number of scripts supported
  26. ScriptGetProperties(NULL, &g_cMaxScript);
  27. }
  28. // Test the OS if it does any complex script.
  29. // REVIEW (keithcu) What if it only supports indic, but not the other ones?
  30. BOOL IsSupportedOS()
  31. {
  32. BOOL fSupport = !OnWin95FE();
  33. int rguCodePage[] = {1255, 1256, 874};
  34. BYTE rgbch[] = {0xe0, 0xd3, 0xa1};
  35. WCHAR rgwch[] = {0x05d0, 0x0633, 0x0e01};
  36. WCHAR wch;
  37. int i = 0;
  38. if (fSupport)
  39. {
  40. for (;i < 3; i++)
  41. {
  42. if (MBTWC(rguCodePage[i], 0, (LPCSTR)&rgbch[i], 1, (LPWSTR)&wch, 1, NULL) > 0 &&
  43. wch == rgwch[i])
  44. break; // support either Arabic, Hebrew or Thai
  45. }
  46. }
  47. return fSupport && i < 3;
  48. }
  49. // Prepare information for digit substitution
  50. // return: Native digit script (shapine engine) ID.
  51. //
  52. WORD CUniscribe::ApplyDigitSubstitution(BYTE bDigitSubstMode)
  53. {
  54. _wesNationalDigit = 0;
  55. // Remember national digits script ID if substitution mode is not None
  56. if (bDigitSubstMode != DIGITS_NOTIMPL && bDigitSubstMode != DIGITS_NONE)
  57. {
  58. WCHAR chZero = 0x0030;
  59. int cItems;
  60. SCRIPT_ITEM si[2];
  61. SCRIPT_CONTROL sc = {0};
  62. SCRIPT_STATE ss = {0};
  63. // force national digit mode
  64. sc.uDefaultLanguage = GetNationalDigitLanguage(GetThreadLocale());
  65. ss.fDigitSubstitute = TRUE;
  66. sc.fContextDigits = FALSE;
  67. if (SUCCEEDED(ScriptItemize(&chZero, 1, 2, &sc, &ss, (SCRIPT_ITEM*)&si, (int*)&cItems)))
  68. _wesNationalDigit = si[0].a.eScript;
  69. }
  70. return _wesNationalDigit;
  71. }
  72. // Some locales may have its own traditional (native) digit and national standard digit
  73. // recognised by a standard body and adopted by NLSAPI. The example is that Nepali(India)
  74. // has its own digit but the India standard uses Hindi digit as the national digit.
  75. //
  76. DWORD CUniscribe::GetNationalDigitLanguage(LCID lcid)
  77. {
  78. DWORD dwDigitLang = PRIMARYLANGID(LANGIDFROMLCID(lcid));
  79. if (W32->OnWinNT5())
  80. {
  81. WCHAR rgwstrDigit[20];
  82. if (GetLocaleInfoW(lcid, LOCALE_SNATIVEDIGITS, rgwstrDigit, sizeof(rgwstrDigit)))
  83. {
  84. // Steal this from Uniscribe (build 0231)
  85. switch (rgwstrDigit[1])
  86. {
  87. case 0x0661: dwDigitLang = LANG_ARABIC; break;
  88. case 0x06F1: dwDigitLang = LANG_FARSI; break;
  89. case 0x0e51: dwDigitLang = LANG_THAI; break;
  90. case 0x0967: dwDigitLang = LANG_HINDI; break;
  91. case 0x09e7: dwDigitLang = LANG_BENGALI; break;
  92. case 0x0a67: dwDigitLang = LANG_PUNJABI; break;
  93. case 0x0ae7: dwDigitLang = LANG_GUJARATI; break;
  94. case 0x0b67: dwDigitLang = LANG_ORIYA; break;
  95. case 0x0be7: dwDigitLang = LANG_TAMIL; break;
  96. case 0x0c67: dwDigitLang = LANG_TELUGU; break;
  97. case 0x0ce7: dwDigitLang = LANG_KANNADA; break;
  98. case 0x0d67: dwDigitLang = LANG_MALAYALAM; break;
  99. case 0x0f21: dwDigitLang = LANG_TIBETAN; break;
  100. case 0x0ed1: dwDigitLang = LANG_LAO; break;
  101. }
  102. }
  103. }
  104. return dwDigitLang;
  105. }
  106. CUniscribe::~CUniscribe ()
  107. {
  108. if (_pFSM)
  109. {
  110. delete _pFSM;
  111. }
  112. }
  113. /***** High level services *****/
  114. // Tokenize string and run Unicode Bidi algorithm if requested.
  115. // return : =<0 - error
  116. // >0 - number of complex script tokens
  117. //
  118. int CUniscribe::ItemizeString (
  119. USP_CLIENT* pc, // in: Working structure
  120. WORD uInitLevel, // in: Initial Bidi level
  121. int* pcItems, // out: Count of items generated
  122. WCHAR* pwchString, // in: Input string
  123. int cch, // in: Number of character to itemize
  124. BOOL fUnicodeBiDi, // in: TRUE - Use UnicodeBidi
  125. WORD wLangId) // in: (optional) Dominant language preference
  126. {
  127. Assert (pc && pc->si && pcItems && pwchString && cch > 0 && cch <= pc->si->cchString);
  128. USP_CLIENT_SI* pc_si = pc->si;
  129. SCRIPT_ITEM* psi = pc_si->psi;
  130. SCRIPT_CONTROL sc = {0};
  131. SCRIPT_STATE ss = {0};
  132. SCRIPT_CONTROL* psc;
  133. SCRIPT_STATE* pss;
  134. HRESULT hr;
  135. int cItems = 0;
  136. if (fUnicodeBiDi)
  137. {
  138. psc = &sc;
  139. pss = &ss;
  140. if (wLangId == LANG_NEUTRAL)
  141. wLangId = PRIMARYLANGID(LANGIDFROMLCID(GetThreadLocale()));
  142. // (preitemize:) set up initial state
  143. psc->uDefaultLanguage = wLangId;
  144. // For Arabic Office's compatibility.
  145. // We enable fArabicNumContext if the dominant language is Arabic.
  146. //
  147. if (psc->uDefaultLanguage == LANG_ARABIC)
  148. pss->fArabicNumContext = uInitLevel & 1;
  149. pss->uBidiLevel = uInitLevel;
  150. // Leave digit substitution to None since we do it ourself.
  151. // pss->fDigitSubstitute = FALSE;
  152. // psc->fContextDigits = FALSE;
  153. }
  154. else
  155. {
  156. psc = NULL;
  157. pss = NULL;
  158. }
  159. // begin real work
  160. hr = ScriptItemize(pwchString, cch, cch+1, psc, pss, psi, (int*)&cItems);
  161. return SUCCEEDED(hr) ? *pcItems = cItems : 0;
  162. }
  163. // Produce a shaped string (glyph array), taking care of font association and measurer's CF update
  164. //
  165. // Success can require 3 calls to Shape():
  166. // 1. Returns E_PENDING (script cache doesn't contain the glyphing information)
  167. // 2. Return USP_E_SCRIPT_NOT_IN_FONT --the HFONT doesn't contain the script needed to do the glyphing
  168. // 3. Hopefully success, but may return again if the fallback font doesn't exist, but we quit anyway.
  169. int CUniscribe::ShapeString (
  170. PLSRUN plsrun, // in: The first run to be shaped
  171. SCRIPT_ANALYSIS* psa, // in: Analysis of the run to be shaped
  172. CMeasurer* pme, // in: Measurer points to start cp of the run
  173. const WCHAR* pwch, // in: String to be shaped
  174. int cch, // in: Count of chars
  175. WORD*& pwgi, // out: Reference to glyph indices array
  176. WORD* pwlc, // out: Logical cluster array
  177. SCRIPT_VISATTR*& psva) // out: Reference to glyph's attribute array
  178. {
  179. AssertSz (plsrun && psa && pme && pwch, "ShapeString failed: Invalid params");
  180. HRESULT hr = S_OK;
  181. HRESULT hrLastError = S_OK;
  182. HDC hdc = NULL;
  183. HFONT hOrgFont = NULL;
  184. int cGlyphs;
  185. int cchAdd = 0;
  186. CCcs *pccsSave = pme->Check_pccs();
  187. int nAttempt = 8; // Maximum attempt to realloc glyph buffer to shape a string
  188. // make sure that we have proper font cache ready to use
  189. if (!pme->_pccs)
  190. return 0;
  191. if (psa->fNoGlyphIndex)
  192. // If no glyph processing, hdc must be around.
  193. hdc = PrepareShapeDC(pme, E_PENDING, hOrgFont);
  194. // prepare glyph buffer
  195. if (!CacheAllocGlyphBuffers(cch, cGlyphs, pwgi, psva))
  196. return 0;
  197. do
  198. {
  199. hr = ScriptShape(hdc, &pme->_pccs->_sc, pwch, cch, cGlyphs, psa, pwgi, pwlc, psva, &cGlyphs);
  200. if (SUCCEEDED(hr))
  201. break;
  202. // Error handling...
  203. switch (hr)
  204. {
  205. case E_PENDING:
  206. case USP_E_SCRIPT_NOT_IN_FONT:
  207. if (hr == hrLastError)
  208. nAttempt = 0; // We encounter the same error twice.
  209. else
  210. {
  211. hdc = PrepareShapeDC(pme, hr, hOrgFont);
  212. hrLastError = hr;
  213. }
  214. break;
  215. case E_OUTOFMEMORY:
  216. // (#6773)Indic shaping engine could produce glyphs more than we could hold.
  217. //
  218. cchAdd += 16;
  219. if (CacheAllocGlyphBuffers(cch + cchAdd, cGlyphs, pwgi, psva))
  220. {
  221. nAttempt--;
  222. break;
  223. }
  224. default:
  225. nAttempt = 0;
  226. //AssertSz(FALSE, "Shaping fails with invalid error or we run out of memory.");
  227. break;
  228. }
  229. } while (nAttempt > 0);
  230. // restore hdc's original font
  231. if (hdc && hOrgFont)
  232. SelectObject(hdc, hOrgFont);
  233. if (pme->_pccs != pccsSave)
  234. plsrun->SetFallback(SUCCEEDED(hr));
  235. return SUCCEEDED(hr) ? cGlyphs : 0;
  236. }
  237. // Place a string and take care of font association and measurer's CF update
  238. //
  239. // This is called right after ShapeString.
  240. int CUniscribe::PlaceString(
  241. PLSRUN plsrun, // in: The first run to be shaped
  242. SCRIPT_ANALYSIS* psa, // in: Analysis of the run to be shaped
  243. CMeasurer* pme, // in: Measurer points to start cp of the run
  244. const WORD* pcwgi, // in: Glyph indices array
  245. int cgi, // in: Count of input glyphs
  246. const SCRIPT_VISATTR* psva, // in: Glyph's attribute array
  247. int* pgdx, // out: Glyph's advanced width array
  248. GOFFSET* pgduv, // out: Glyph's offset array
  249. ABC* pABC) // out: Run's dimension
  250. {
  251. AssertSz (plsrun && psa && pme && pcwgi, "PlaceString failed: Invalid params");
  252. HRESULT hr = S_OK;
  253. HRESULT hrLastError = S_OK;
  254. HDC hdc = NULL;
  255. HFONT hOrgFont = NULL;
  256. int nAttempt = 1;
  257. pme->Check_pccs();
  258. pme->ApplyFontCache(plsrun->IsFallback());
  259. // make sure that we have proper font cache ready to use
  260. if (!pme->_pccs)
  261. return 0;
  262. if (psa->fNoGlyphIndex)
  263. // If no glyph processing, hdc must be around.
  264. hdc = PrepareShapeDC(pme, E_PENDING, hOrgFont);
  265. do
  266. {
  267. hr = ScriptPlace(hdc, &pme->_pccs->_sc, pcwgi, cgi, psva, psa, pgdx, pgduv, pABC);
  268. if (SUCCEEDED(hr))
  269. break;
  270. // Error handling...
  271. switch (hr)
  272. {
  273. case E_PENDING:
  274. if (hr == hrLastError)
  275. nAttempt = 0; // We encounter the same error twice.
  276. else
  277. {
  278. hdc = PrepareShapeDC(pme, hr, hOrgFont);
  279. hrLastError = hr;
  280. }
  281. break;
  282. default:
  283. nAttempt = 0;
  284. //AssertSz(FALSE, "Placing fails with invalid error.");
  285. break;
  286. }
  287. } while (nAttempt > 0);
  288. // restore hdc's original font
  289. if (hdc && hOrgFont)
  290. SelectObject(hdc, hOrgFont);
  291. return SUCCEEDED(hr) ? cgi : 0;
  292. }
  293. // Placing given string results in logical width array,
  294. // the result array would be used to record WMF metafile.
  295. //
  296. int CUniscribe::PlaceMetafileString (
  297. PLSRUN plsrun, // in: The first run to be shaped
  298. CMeasurer* pme, // in: Measurer points to start cp of the run
  299. const WCHAR* pwch, // in: Input codepoint string
  300. int cch, // in: Character count
  301. PINT* ppiDx) // out: Pointer to logical widths array
  302. {
  303. AssertSz (pme && pwch && ppiDx, "PlaceMetafileString failed: Invalid params");
  304. if (W32->OnWinNT4() || W32->OnWin9xThai())
  305. {
  306. // MET NT40 has bug in lpdx justification so i doesnt playback the lpdx very nicely.
  307. // Thai Win9x simply cannot handle fancy lpdx values generated by Uniscribe.
  308. // We workaround both cases here by metafiling no lpdx and let the system reconstructs
  309. // it from scratch during playback time.
  310. // =FUTURE= If we do line justification. We need more sophisticated work here
  311. // basically to reconstruct the OS preferred type of lpdx.
  312. //
  313. *ppiDx = NULL;
  314. return cch;
  315. }
  316. HRESULT hr = S_OK;
  317. PUSP_CLIENT pc = NULL;
  318. int* piDx; // result logical width array
  319. int cgi = 0;
  320. BYTE pbBufIn[MAX_CLIENT_BUF];
  321. SCRIPT_ANALYSIS sa = plsrun->_a;
  322. BOOL fVisualGlyphDx = sa.fRTL && W32->OnWin9x() && W32->OnBiDiOS();
  323. // Get static buffer for logical widths array
  324. if (!(piDx = GetWidthBuffer(cch)))
  325. return 0;
  326. CreateClientStruc(pbBufIn, MAX_CLIENT_BUF, &pc, cch, cli_ShapePlace);
  327. if (!pc)
  328. return 0;
  329. PUSP_CLIENT_SSP pcssp = pc->ssp;
  330. if (fVisualGlyphDx)
  331. sa.fLogicalOrder = FALSE; // shaping result in visual order
  332. // Shape string
  333. if (cgi = ShapeString(plsrun, &sa, pme, pwch, (int)cch, pcssp->pwgi, pcssp->pcluster, pcssp->psva))
  334. {
  335. // then place it...
  336. if (cgi == PlaceString(plsrun, &sa, pme, pcssp->pwgi, cgi, pcssp->psva, pcssp->pidx, pcssp->pgoffset, NULL))
  337. {
  338. if (fVisualGlyphDx)
  339. {
  340. // Workaround BiDi Win9x's lpdx handling
  341. // It assumes ExtTextOut's dx array is glyph width in visual order
  342. Assert (cgi <= cch); // glyph count never exceeds character count in BiDi
  343. CopyMemory (piDx, pcssp->pidx, min(cgi, cch)*sizeof(int));
  344. }
  345. else
  346. {
  347. // Map visual glyph widths to logical widths
  348. hr = ScriptGetLogicalWidths(&sa, cch, cgi, pcssp->pidx, pcssp->pcluster, pcssp->psva, piDx);
  349. }
  350. }
  351. }
  352. // result
  353. *ppiDx = piDx;
  354. if (pc && pbBufIn != (BYTE*)pc)
  355. FreePv(pc);
  356. return SUCCEEDED(hr) ? cgi : 0;
  357. }
  358. /***** Helper functions *****/
  359. // Retrieve the BidiLevel FSM
  360. const CBiDiFSM* CUniscribe::GetFSM ()
  361. {
  362. if (!_pFSM)
  363. {
  364. _pFSM = new CBiDiFSM(this);
  365. if (_pFSM && !_pFSM->Init())
  366. {
  367. delete _pFSM;
  368. }
  369. }
  370. return _pFSM;
  371. }
  372. // Prepare the shapeable font ready to dc for a given script
  373. //
  374. // USP_E_SCRIPT_NOT_IN_FONT - complex scripts font association
  375. // E_PENDING - prepare dc with current font selected
  376. //
  377. HDC CUniscribe::PrepareShapeDC (
  378. CMeasurer* pme, // in: Measurer points to start cp of the run
  379. HRESULT hrReq, // in: Error code to react
  380. HFONT& hOrgFont) // in/out: Original font of the shape DC
  381. {
  382. Assert (pme);
  383. HDC hdc = NULL;
  384. HFONT hOldFont;
  385. switch (hrReq)
  386. {
  387. case USP_E_SCRIPT_NOT_IN_FONT:
  388. {
  389. pme->ApplyFontCache(fTrue);
  390. #ifdef DEBUG
  391. if (pme->_pccs)
  392. Tracef(TRCSEVWARN, "USP_E_SCRIPT_NOT_IN_FONT: charset %d applied", pme->_pccs->_bCharSet);
  393. #endif
  394. }
  395. default:
  396. if (pme->_pccs)
  397. {
  398. hdc = pme->_pccs->_hdc;
  399. hOldFont = (HFONT)SelectObject(hdc, pme->_pccs->_hfont);
  400. if (!hOrgFont)
  401. hOrgFont = hOldFont;
  402. }
  403. }
  404. return hdc;
  405. }
  406. const SCRIPT_PROPERTIES* CUniscribe::GeteProp (WORD eScript)
  407. {
  408. if (!_ppProp)
  409. {
  410. if (!SUCCEEDED(ScriptGetProperties(&_ppProp, NULL)) || !_ppProp)
  411. _ppProp = g_pPropUndef;
  412. }
  413. if (_ppProp == g_pPropUndef || eScript >= (WORD)g_cMaxScript)
  414. eScript = 0;
  415. return _ppProp[eScript];
  416. }
  417. // Figure proper charset to use for complex script.
  418. // The resulted charset can be either actual or virtual (internal) GDI charset used by given script
  419. BOOL CUniscribe::GetComplexCharSet(
  420. const SCRIPT_PROPERTIES* psp, // Uniscribe script's properties
  421. BYTE bCharSetDefault, // -1 format's charset
  422. BYTE& bCharSetOut) // out: Charset to use
  423. {
  424. Assert(psp);
  425. BYTE bCharSet = !psp->fCDM ? psp->bCharSet : GetCDMCharSet(bCharSetDefault);
  426. BOOL fr = psp->fComplex && !psp->fControl;
  427. if (fr)
  428. {
  429. bCharSetOut = IN_RANGE(ANSI_CHARSET, bCharSet, DEFAULT_CHARSET) ?
  430. GetCharSet(ConvertLanguageIDtoCodePage(psp->langid), NULL) :
  431. bCharSet;
  432. }
  433. return fr;
  434. }
  435. // Figure out the charset to use for CDM run
  436. //
  437. BYTE CUniscribe::GetCDMCharSet(BYTE bCharSetDefault)
  438. {
  439. if (!_bCharSetCDM)
  440. {
  441. _bCharSetCDM = (bCharSetDefault == VIETNAMESE_CHARSET ||
  442. W32->GetPreferredKbd(VIET_INDEX) ||
  443. GetCharSet(GetLocaleCodePage(), NULL) == VIETNAMESE_CHARSET ||
  444. GetCharSet(GetACP(), NULL) == VIETNAMESE_CHARSET) ? VIETNAMESE_CHARSET : DEFAULT_CHARSET;
  445. }
  446. return _bCharSetCDM;
  447. }
  448. BYTE CUniscribe::GetRtlCharSet(CTxtEdit* ped)
  449. {
  450. if (!_bCharSetRtl)
  451. {
  452. // First, try default charset
  453. DWORD dwCharFlags;
  454. BYTE bCharSet = ped->GetCharFormat(-1)->_bCharSet;
  455. if (!IN_RANGE(HEBREW_CHARSET, bCharSet, ARABIC_CHARSET))
  456. {
  457. // then the system charset
  458. bCharSet = GetCharSet(GetACP(), NULL);
  459. if (!IN_RANGE(HEBREW_CHARSET, bCharSet, ARABIC_CHARSET))
  460. {
  461. // then the content
  462. dwCharFlags = ped->GetCharFlags() & (fARABIC | fHEBREW);
  463. if (dwCharFlags == fARABIC)
  464. bCharSet = ARABIC_CHARSET;
  465. else if(dwCharFlags == fHEBREW)
  466. bCharSet = HEBREW_CHARSET;
  467. else
  468. {
  469. // and last chance with the first found loaded Bidi kbd
  470. if (W32->GetPreferredKbd(HEBREW_INDEX))
  471. bCharSet = HEBREW_CHARSET;
  472. else
  473. // Even if we cant find Arabic, we have to assume it here.
  474. bCharSet = ARABIC_CHARSET;
  475. }
  476. }
  477. }
  478. Assert(IsBiDiCharSet(bCharSet));
  479. _bCharSetRtl = bCharSet;
  480. }
  481. return _bCharSetRtl;
  482. }
  483. // Substitute digit shaper in plsrun if needed
  484. //
  485. void CUniscribe::SubstituteDigitShaper (
  486. PLSRUN plsrun,
  487. CMeasurer* pme)
  488. {
  489. Assert(plsrun && pme);
  490. CTxtEdit* ped = pme->GetPed();
  491. WORD wScript;
  492. if (GeteProp(plsrun->_a.eScript)->fNumeric)
  493. {
  494. wScript = plsrun->_pCF->_wScript; // reset it before
  495. switch (W32->GetDigitSubstitutionMode())
  496. {
  497. case DIGITS_CTX:
  498. {
  499. if (ped->IsRich())
  500. {
  501. // Context mode simply means the charset of the kbd for richtext.
  502. if (!IsBiDiCharSet(ped->GetCharFormat(pme->_rpCF.GetFormat())->_bCharSet))
  503. break;
  504. }
  505. else
  506. {
  507. // Digit follows directionality of preceding run for plain text
  508. CFormatRunPtr rp(pme->_rpCF);
  509. Assert(rp.IsValid());
  510. if (rp.PrevRun())
  511. {
  512. if (!IsBiDiCharSet(ped->GetCharFormat(rp.GetFormat())->_bCharSet))
  513. break;
  514. }
  515. else
  516. {
  517. // No preceding run, looking for the paragraph direction
  518. if (!pme->Get_pPF()->IsRtlPara())
  519. break;
  520. }
  521. }
  522. // otherwise, fall thru...
  523. }
  524. case DIGITS_NATIONAL:
  525. wScript = _wesNationalDigit;
  526. default:
  527. break;
  528. }
  529. // Update all linked runs
  530. while (plsrun)
  531. {
  532. plsrun->_a.eScript = wScript; // assign proper shaping engine to digits
  533. plsrun = plsrun->_pNext;
  534. }
  535. }
  536. }
  537. /***** Uniscribe entry point *****/
  538. // memory allocator
  539. //
  540. BOOL CUniscribe::CreateClientStruc (
  541. BYTE* pbBufIn,
  542. LONG cbBufIn,
  543. PUSP_CLIENT* ppc,
  544. LONG cchString,
  545. DWORD dwMask)
  546. {
  547. Assert(ppc && pbBufIn);
  548. if (!ppc)
  549. return FALSE;
  550. *ppc = NULL;
  551. if (cchString == 0)
  552. cchString = 1; // simplify caller's logic
  553. LONG i;
  554. LONG cbSize;
  555. PBYTE pbBlock;
  556. // ScriptItemize's
  557. //
  558. PVOID pvString;
  559. PVOID pvsi;
  560. // ScriptBreak's
  561. //
  562. PVOID pvsla;
  563. // ScriptShape & Place's
  564. //
  565. PVOID pvwgi;
  566. PVOID pvsva;
  567. PVOID pvcluster;
  568. PVOID pvidx;
  569. PVOID pvgoffset;
  570. // subtable ptrs
  571. //
  572. PUSP_CLIENT_SI pc_si;
  573. PUSP_CLIENT_SB pc_sb;
  574. PUSP_CLIENT_SSP pc_ssp;
  575. #define RQ_COUNT 12
  576. BUF_REQ brq[RQ_COUNT] =
  577. {
  578. // table and subtable blocks
  579. //
  580. { sizeof(USP_CLIENT), 1, (void**)ppc},
  581. { sizeof(USP_CLIENT_SI), dwMask & cli_Itemize ? 1 : 0, (void**)&pc_si},
  582. { sizeof(USP_CLIENT_SB), dwMask & cli_Break ? 1 : 0, (void**)&pc_sb},
  583. { sizeof(USP_CLIENT_SSP), dwMask & cli_ShapePlace ? 1 : 0, (void**)&pc_ssp},
  584. // data blocks
  585. //
  586. { sizeof(WCHAR), dwMask & cli_string ? cchString + 1 : 0, &pvString},
  587. { sizeof(SCRIPT_ITEM), dwMask & cli_psi ? cchString + 1 : 0, &pvsi},
  588. { sizeof(SCRIPT_LOGATTR), dwMask & cli_psla ? cchString + 1 : 0, &pvsla},
  589. { sizeof(WORD), dwMask & cli_pwgi ? GLYPH_COUNT(cchString+1) : 0, &pvwgi},
  590. { sizeof(SCRIPT_VISATTR), dwMask & cli_psva ? GLYPH_COUNT(cchString+1) : 0, &pvsva},
  591. { sizeof(WORD), dwMask & cli_pcluster ? cchString + 1 : 0, &pvcluster},
  592. { sizeof(int), dwMask & cli_pidx ? GLYPH_COUNT(cchString+1) : 0, &pvidx},
  593. { sizeof(GOFFSET), dwMask & cli_pgoffset ? GLYPH_COUNT(cchString+1) : 0, &pvgoffset},
  594. };
  595. // count total buffer size in byte (WORD aligned)
  596. //
  597. for (i=0, cbSize=0; i < RQ_COUNT; i++)
  598. {
  599. cbSize += ALIGN(brq[i].size * brq[i].c);
  600. }
  601. // allocate the whole buffer at once
  602. //
  603. if (cbSize > cbBufIn)
  604. {
  605. pbBlock = (PBYTE)PvAlloc(cbSize, 0);
  606. }
  607. else
  608. {
  609. pbBlock = pbBufIn;
  610. }
  611. if (!pbBlock)
  612. {
  613. //
  614. // memory management failed!
  615. //
  616. TRACEERRORSZ("Allocation failed in CreateClientStruc!\n");
  617. *ppc = NULL;
  618. return FALSE;
  619. }
  620. // clear the main table
  621. ZeroMemory (pbBlock, sizeof(USP_CLIENT));
  622. // assign ptrs in buffer request structure
  623. //
  624. for (i=0; i < RQ_COUNT; i++)
  625. {
  626. if (brq[i].c > 0)
  627. {
  628. *brq[i].ppv = pbBlock;
  629. pbBlock += ALIGN(brq[i].size * brq[i].c);
  630. }
  631. else
  632. {
  633. *brq[i].ppv = NULL;
  634. }
  635. }
  636. Assert(((PBYTE)(*ppc)+cbSize == pbBlock));
  637. // fill in data block ptrs in subtable
  638. //
  639. if (pc_si)
  640. {
  641. pc_si->pwchString = (WCHAR*) pvString;
  642. pc_si->cchString = cchString;
  643. pc_si->psi = (SCRIPT_ITEM*) pvsi;
  644. }
  645. if (pc_sb)
  646. {
  647. pc_sb->psla = (SCRIPT_LOGATTR*) pvsla;
  648. }
  649. if (pc_ssp)
  650. {
  651. pc_ssp->pwgi = (WORD*) pvwgi;
  652. pc_ssp->psva = (SCRIPT_VISATTR*) pvsva;
  653. pc_ssp->pcluster = (WORD*) pvcluster;
  654. pc_ssp->pidx = (int*) pvidx;
  655. pc_ssp->pgoffset = (GOFFSET*) pvgoffset;
  656. }
  657. // fill in subtable ptrs in header table
  658. //
  659. (*ppc)->si = (PUSP_CLIENT_SI) pc_si;
  660. (*ppc)->sb = (PUSP_CLIENT_SB) pc_sb;
  661. (*ppc)->ssp = (PUSP_CLIENT_SSP) pc_ssp;
  662. return TRUE;
  663. }
  664. /////// CBidiFSM class implementation
  665. //
  666. // Create: Worachai Chaoweeraprasit(wchao), Jan 29, 1998
  667. //
  668. CBiDiFSM::~CBiDiFSM ()
  669. {
  670. FreePv(_pStart);
  671. }
  672. INPUT_CLASS CBiDiFSM::InputClass (
  673. const CCharFormat* pcCF,
  674. CTxtPtr* ptp,
  675. LONG cchRun) const
  676. {
  677. if (!_pusp->IsValid() || !pcCF || pcCF->_wScript == SCRIPT_WHITE)
  678. return chGround;
  679. const SCRIPT_PROPERTIES* psp = _pusp->GeteProp(pcCF->_wScript);
  680. BYTE bCharSet = pcCF->_bCharSet;
  681. if (psp->fControl)
  682. {
  683. if (cchRun == 1)
  684. switch (ptp->GetChar()) // single-char run
  685. {
  686. case LTRMARK: return chLTR; // \ltrmark
  687. case RTLMARK: return chRTL; // \rtlmark
  688. }
  689. return chGround;
  690. }
  691. if (IsSymbolOrOEM(bCharSet) || IsFECharSet(bCharSet) || pcCF->_dwEffects & CFE_RUNISDBCS)
  692. return chLTR;
  693. if (psp->fNumeric)
  694. // Numeric digits
  695. return IsBiDiCharSet(psp->bCharSet) || IsBiDiCharSet(bCharSet) ? digitRTL : digitLTR;
  696. // RTL if it's RTL script or its format charset is RTL and NOT a simplified script
  697. return IsBiDiCharSet(psp->bCharSet) || pcCF->_wScript && IsBiDiCharSet(bCharSet) ? chRTL : chLTR;
  698. }
  699. // The FSM generates run's embedding level based on given base level and puts it
  700. // in CFormatRun. LsFetchRun is the client using this result.
  701. //
  702. #ifdef DEBUG
  703. //#define DEBUG_LEVEL
  704. #endif
  705. #ifdef DEBUG_LEVEL
  706. void DebugLevel (CBiDiFSMCell* pCell)
  707. {
  708. Tracef(TRCSEVNONE, "%d,", pCell->_level._value);
  709. }
  710. #else
  711. #define DebugLevel(x)
  712. #endif
  713. HRESULT CBiDiFSM::RunFSM (
  714. CRchTxtPtr* prtp, // in: text pointer to start run
  715. LONG cRuns, // in: number of FSM run
  716. LONG cRunsStart, // in: number of start run
  717. BYTE bBaseLevel) const // in: base level
  718. {
  719. Assert (prtp->_rpCF.IsValid() && cRuns > 0);
  720. CRchTxtPtr rtp(*prtp);
  721. const CCharFormat* pCF;
  722. LONG cchRun;
  723. LONG cRunsAll = cRuns + cRunsStart;
  724. CBiDiFSMCell* pCell;
  725. USHORT ucState = bBaseLevel ? S_X * NUM_FSM_INPUTS : 0;
  726. BOOL fNext = TRUE;
  727. // loop thru FSM
  728. for (; fNext && cRunsAll > 0; cRunsAll--, fNext = !!rtp.Advance(cchRun))
  729. {
  730. cchRun = rtp.GetCchLeftRunCF();
  731. pCF = rtp.GetPed()->GetCharFormat(rtp._rpCF.GetFormat());
  732. ucState += InputClass(pCF, &rtp._rpTX, cchRun);
  733. pCell = &_pStart[ucState];
  734. // set level to FSM runs
  735. if (cRunsAll <= cRuns)
  736. rtp._rpCF.SetLevel (pCell->_level);
  737. DebugLevel(pCell);
  738. ucState = pCell->_uNext; // next state
  739. }
  740. return S_OK;
  741. }
  742. // Construct the BiDi embedding level FSM (FSM details see bidifsm2.html)
  743. // :FSM's size = NUM_FSM_INPUTS * NUM_FSM_STATES * sizeof(CBiDiFSMCell) = 6*5*4 = 120 bytes
  744. //
  745. BOOL CBiDiFSM::Init()
  746. {
  747. CBiDiFSMCell* pCell;
  748. int i;
  749. // Build the Bidi FSM
  750. _nState = NUM_FSM_STATES;
  751. _nInput = NUM_FSM_INPUTS;
  752. pCell = (CBiDiFSMCell*)PvAlloc(NUM_FSM_STATES * NUM_FSM_INPUTS * sizeof(CBiDiFSMCell), 0);
  753. if (!pCell)
  754. return FALSE; // unable to create FSM!
  755. _pStart = pCell;
  756. CBiDiLevel lvlZero = {0,0};
  757. CBiDiLevel lvlOne = {1,0};
  758. CBiDiLevel lvlTwo = {2,0};
  759. CBiDiLevel lvlTwoStart = {2,1};
  760. // State A(0): LTR char in LTR para
  761. //
  762. for (i=0; i < NUM_FSM_INPUTS; i++, pCell++)
  763. {
  764. switch (i)
  765. {
  766. case chLTR:
  767. SetFSMCell(pCell, &lvlZero, 0); break;
  768. case chRTL:
  769. SetFSMCell(pCell, &lvlOne, S_B * NUM_FSM_INPUTS); break;
  770. case digitLTR:
  771. SetFSMCell(pCell, &lvlZero, 0); break;
  772. case digitRTL:
  773. SetFSMCell(pCell, &lvlTwo, S_C * NUM_FSM_INPUTS); break;
  774. case chGround:
  775. SetFSMCell(pCell, &lvlZero, 0); break;
  776. }
  777. }
  778. // State B(1): RTL char in LTR para
  779. //
  780. for (i=0; i < NUM_FSM_INPUTS; i++, pCell++)
  781. {
  782. switch (i)
  783. {
  784. case chLTR:
  785. SetFSMCell(pCell, &lvlZero, 0); break;
  786. case chRTL:
  787. SetFSMCell(pCell, &lvlOne, S_B * NUM_FSM_INPUTS); break;
  788. case digitLTR:
  789. SetFSMCell(pCell, &lvlZero, 0); break;
  790. case digitRTL:
  791. SetFSMCell(pCell, &lvlTwo, S_C * NUM_FSM_INPUTS); break;
  792. case chGround:
  793. SetFSMCell(pCell, &lvlZero, 0); break;
  794. }
  795. }
  796. // State C(2): RTL number run in LTR para
  797. //
  798. for (i=0; i < NUM_FSM_INPUTS; i++, pCell++)
  799. {
  800. switch (i)
  801. {
  802. case chLTR:
  803. SetFSMCell(pCell, &lvlZero, 0); break;
  804. case chRTL:
  805. SetFSMCell(pCell, &lvlOne, S_B * NUM_FSM_INPUTS); break;
  806. case digitLTR:
  807. SetFSMCell(pCell, &lvlZero, 0); break;
  808. case digitRTL:
  809. SetFSMCell(pCell, &lvlTwo, S_C * NUM_FSM_INPUTS); break;
  810. case chGround:
  811. SetFSMCell(pCell, &lvlZero, 0); break;
  812. }
  813. }
  814. // State X(1): RTL char in RTL para
  815. //
  816. for (i=0; i < NUM_FSM_INPUTS; i++, pCell++)
  817. {
  818. switch (i)
  819. {
  820. case chLTR:
  821. SetFSMCell(pCell, &lvlTwo, S_Y * NUM_FSM_INPUTS); break;
  822. case chRTL:
  823. SetFSMCell(pCell, &lvlOne, S_X * NUM_FSM_INPUTS); break;
  824. case digitLTR:
  825. SetFSMCell(pCell, &lvlTwo, S_Y * NUM_FSM_INPUTS); break;
  826. case digitRTL:
  827. SetFSMCell(pCell, &lvlTwo, S_Z * NUM_FSM_INPUTS); break;
  828. case chGround:
  829. SetFSMCell(pCell, &lvlOne, S_X * NUM_FSM_INPUTS); break;
  830. }
  831. }
  832. // State Y(2): LTR char in RTL para
  833. //
  834. for (i=0; i < NUM_FSM_INPUTS; i++, pCell++)
  835. {
  836. switch (i)
  837. {
  838. case chLTR:
  839. SetFSMCell(pCell, &lvlTwo, S_Y * NUM_FSM_INPUTS); break;
  840. case chRTL:
  841. SetFSMCell(pCell, &lvlOne, S_X * NUM_FSM_INPUTS); break;
  842. case digitLTR:
  843. SetFSMCell(pCell, &lvlTwo, S_Y * NUM_FSM_INPUTS); break;
  844. case digitRTL:
  845. SetFSMCell(pCell, &lvlTwoStart, S_Z * NUM_FSM_INPUTS); break;
  846. case chGround:
  847. SetFSMCell(pCell, &lvlOne, S_X * NUM_FSM_INPUTS); break;
  848. }
  849. }
  850. // State Z(2): RTL number in RTL para
  851. //
  852. for (i=0; i < NUM_FSM_INPUTS; i++, pCell++)
  853. {
  854. switch (i)
  855. {
  856. case chLTR:
  857. SetFSMCell(pCell, &lvlTwoStart, S_Y * NUM_FSM_INPUTS); break;
  858. case chRTL:
  859. SetFSMCell(pCell, &lvlOne, S_X * NUM_FSM_INPUTS); break;
  860. case digitLTR:
  861. SetFSMCell(pCell, &lvlTwoStart, S_Y * NUM_FSM_INPUTS); break;
  862. case digitRTL:
  863. SetFSMCell(pCell, &lvlTwo, S_Z * NUM_FSM_INPUTS); break;
  864. case chGround:
  865. SetFSMCell(pCell, &lvlOne, S_X * NUM_FSM_INPUTS); break;
  866. }
  867. }
  868. AssertSz(&pCell[-(NUM_FSM_STATES * NUM_FSM_INPUTS)] == _pStart, "Bidi FSM incomplete constructed!");
  869. return TRUE;
  870. }
  871. /////// CCallbackBufferBase class implementation
  872. //
  873. void* CBufferBase::GetPtr(int cel)
  874. {
  875. if (_cElem < cel)
  876. {
  877. cel += celAdvance;
  878. _p = PvReAlloc(_p, cel * _cbElem);
  879. if (!_p)
  880. return NULL;
  881. ZeroMemory(_p, cel * _cbElem);
  882. _cElem = cel;
  883. }
  884. return _p;
  885. }
  886. void CBufferBase::Release()
  887. {
  888. if (_p)
  889. FreePv(_p);
  890. }