Leaked source code of windows server 2003
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.

2994 lines
80 KiB

  1. /*
  2. * @doc INTERNAL
  3. *
  4. * @module OLS.CPP -- COls LineServices object class
  5. *
  6. * Authors:
  7. * Murray Sargent: initial coding up to nonLS RichEdit functionality
  8. * (with lots of help from RickSa's ols code)
  9. * Keith Curtis and Worachai Chaoweeraprasit: complex script support,
  10. * etc.
  11. *
  12. * @todo
  13. * LSCHP.dcpMaxContext is never set for complex scripts!
  14. *
  15. * Copyright (c) 1997-2000 Microsoft Corporation. All rights reserved.
  16. */
  17. #include "_common.h"
  18. #ifndef NOLINESERVICES
  19. #include "_edit.h"
  20. #include "_font.h"
  21. #include "_render.h"
  22. #include "_osdc.h"
  23. #include "_tomfmt.h"
  24. #include "_ols.h"
  25. #include "_clasfyc.h"
  26. #include "_uspi.h"
  27. #include "_txtbrk.h"
  28. #include "_hyph.h"
  29. ASSERTDATA
  30. // Guess at the number of characters on the line
  31. const int cchLineHint = 66;
  32. #define OBJID_OLE 0
  33. #define OBJID_REVERSE 1
  34. #define OBJID_COUNT 2
  35. const WCHAR wchObjectEnd = 0x9F;
  36. const WCHAR rgchObjectEnd[] = {wchObjectEnd};
  37. #define MAX_OBJ_DEPTH 3
  38. extern const LSCBK lscbk;
  39. // Kinsoku break pair information
  40. extern const INT g_cKinsokuCategories;
  41. CLineServices *g_plsc = NULL; // LineServices Context
  42. COls* g_pols = NULL; // COls ptr
  43. const LSBRK rglsbrkDefault[] =
  44. {
  45. 0,0, // Always prohibited
  46. 0,1, // OK across blanks
  47. 1,1 // Always allowed
  48. };
  49. // prototypes
  50. void EmitBrace(COls* pols, PLSCHP pchp, BOOL* pfHid, DWORD* pcch, PLSRUN* pprun, LPCWSTR* plpwch, int id, LPCWSTR str);
  51. void DupShapeState(PLSRUN prun, LONG cch);
  52. // public inline functions
  53. //
  54. // Emitting fake brace to LS
  55. inline void EmitBrace(
  56. COls* pols,
  57. PLSCHP pchp,
  58. BOOL* pfHid,
  59. DWORD* pcch,
  60. PLSRUN* pprun,
  61. LPCWSTR* plpwch,
  62. int id,
  63. LPCWSTR str)
  64. {
  65. ZeroMemory(pchp, sizeof(*pchp));
  66. pchp->idObj = (WORD)id;
  67. *pfHid = 0;
  68. *pcch = 1;
  69. *pprun = pols->GetPlsrun(0, pols->_pme->GetCF(), FALSE);
  70. *plpwch = str;
  71. }
  72. #ifndef NOCOMPLEXSCRIPTS
  73. // Duplicate shaping state to each runs in the chain
  74. // note: this macro used only by GetGlyph and GetGlyphPosition
  75. inline void DupShapeState(
  76. PLSRUN prun,
  77. LONG cch)
  78. {
  79. PLSRUN pnext = prun->_pNext;
  80. LONG cpEnd = prun->_cp + cch;
  81. while (pnext && pnext->_cp < cpEnd)
  82. {
  83. CopyMemory(&pnext->_a, &prun->_a, sizeof(SCRIPT_ANALYSIS));
  84. pnext->SetFallback(prun->IsFallback());
  85. prun = pnext;
  86. pnext = prun->_pNext;
  87. }
  88. Assert(!pnext && prun->_cp < cpEnd);
  89. }
  90. #endif
  91. LONG COls::GetCpLsFromCpRe(
  92. LONG cpRe)
  93. {
  94. if (_rgcp.Count() == 0)
  95. return cpRe;
  96. LONG *pcp = _rgcp.Elem(0);
  97. for(LONG cpLs = cpRe; cpLs >= *pcp; pcp++)
  98. cpLs++;
  99. return cpLs;
  100. }
  101. LONG COls::GetCpReFromCpLs(
  102. LONG cpLs)
  103. {
  104. if (_rgcp.Count() == 0)
  105. return cpLs;
  106. LONG *pcp = _rgcp.Elem(0);
  107. for(int dcp = 0; cpLs > *pcp; pcp++)
  108. dcp--;
  109. return cpLs + dcp;
  110. }
  111. #ifdef DEBUG
  112. //#define DEBUG_BRACE
  113. #endif
  114. // return TRUE if braces added
  115. BOOL COls::AddBraceCp(long cpLs)
  116. {
  117. if (_rgcp.Count() == 0)
  118. {
  119. long *pcp = _rgcp.Insert(0, 1);
  120. *pcp = tomForward;
  121. }
  122. long *pcp = _rgcp.Elem(0);
  123. long iel = 0;
  124. while (cpLs > pcp[iel])
  125. iel++;
  126. if (cpLs < pcp[iel])
  127. {
  128. pcp = _rgcp.Insert(iel, 1);
  129. *pcp = cpLs;
  130. return TRUE;
  131. }
  132. return FALSE;
  133. }
  134. // return number of braces before cp
  135. //
  136. LONG COls::BracesBeforeCp(
  137. LONG cpLs)
  138. {
  139. LONG iel, cbr = 0;
  140. LONG* pcp;
  141. if (!cpLs || (iel = _rgcp.Count()) < 2)
  142. return 0;
  143. iel -= 2; // exclude the last tomForward one and make a count an index
  144. cpLs--; // start with the cp preceding given cp
  145. pcp = _rgcp.Elem(0);
  146. while (iel > -1 && pcp[iel] > cpLs) // search the first one
  147. iel--;
  148. while (iel > -1 && pcp[iel] == cpLs) // continue counting
  149. {
  150. iel--;
  151. cpLs--;
  152. cbr++;
  153. }
  154. return cbr;
  155. }
  156. /*
  157. * COls::SetRun(plsrun)
  158. *
  159. * @mfunc
  160. * Do whatever is needed to initialize the measurer (pme) to the lsrun
  161. * givin by plsrun and return whether the run is for autonumbering.
  162. *
  163. * @rdesc
  164. * TRUE if plsrun refers to an autonumbering run
  165. */
  166. BOOL COls::SetRun(
  167. PLSRUN plsrun)
  168. {
  169. LONG cp = plsrun->_cp;
  170. _pme->SetCp(cp & 0x7FFFFFFF);
  171. return plsrun->IsBullet();
  172. }
  173. /*
  174. * CLsrun::IsSelected()
  175. *
  176. * @mfunc
  177. * return whether or not the run should be drawn as selected.
  178. *
  179. * @rdesc
  180. * TRUE if run should be drawn with selection colors
  181. */
  182. BOOL CLsrun::IsSelected()
  183. {
  184. if (!_fSelected)
  185. return FALSE;
  186. CRenderer *pre = g_pols->GetRenderer();
  187. Assert(pre->IsRenderer());
  188. return pre->_fRenderSelection ? TRUE : FALSE;
  189. }
  190. /*
  191. * COls::CreatePlsrun ()
  192. *
  193. * @mfunc
  194. * Creates a PLSRUN. Is a little tricky because we allocate them
  195. * in chunks.
  196. *
  197. * @rdesc
  198. * plsrun
  199. */
  200. const int cplsrunAlloc = 8;
  201. PLSRUN COls::CreatePlsrun()
  202. {
  203. CLsrunChunk *plsrunChunk = 0;
  204. //First, find a chunk to use
  205. int cchunk = _rglsrunChunk.Count();
  206. for (int ichunk = 0; cchunk && ichunk < cchunk; ichunk++)
  207. {
  208. plsrunChunk = _rglsrunChunk.Elem(ichunk);
  209. if (plsrunChunk->_cel < cplsrunAlloc)
  210. break;
  211. }
  212. if (!cchunk || ichunk == cchunk || plsrunChunk->_cel == cplsrunAlloc)
  213. {
  214. CLsrun *rglsrun = new CLsrun[cplsrunAlloc];
  215. if (rglsrun)
  216. {
  217. plsrunChunk = _rglsrunChunk.Add(1, 0);
  218. if (!plsrunChunk)
  219. {
  220. delete[] rglsrun;
  221. return 0;
  222. }
  223. plsrunChunk->_prglsrun = rglsrun;
  224. }
  225. else
  226. return 0;
  227. }
  228. return &plsrunChunk->_prglsrun[plsrunChunk->_cel++];
  229. }
  230. /*
  231. * GetPlsrun(cp, pCF, fAutoNumber)
  232. *
  233. * @func
  234. * Return plsrun for info in run. The structure contains the starting cp
  235. * of the run and the script analysis if Uniscribe is activated. The
  236. * analysis information is needed by subsequent callbacks - GetGlyphs and
  237. * GetGlyphPositions to be passed to Uniscribe in order to shape and
  238. * position glyphs correctly for complex scripts.
  239. *
  240. * @rdesc
  241. * plsrun corresponding to info in arguments
  242. */
  243. PLSRUN COls::GetPlsrun(
  244. LONG cp,
  245. const CCharFormat *pCF,
  246. BOOL fAutoNumber)
  247. {
  248. if(fAutoNumber)
  249. cp |= CP_BULLET;
  250. CLsrun *plsrun = CreatePlsrun();
  251. if (plsrun)
  252. {
  253. ZeroMemory(plsrun, sizeof(CLsrun));
  254. plsrun->_pCF = pCF;
  255. plsrun->_cp = fAutoNumber ? _cp | CP_BULLET : cp;
  256. LONG cpSelMin, cpSelMost;
  257. _pme->GetPed()->GetSelRangeForRender(&cpSelMin, &cpSelMost);
  258. plsrun->SetSelected(!plsrun->IsBullet() && cp >= cpSelMin && cp < cpSelMost);
  259. #ifndef NOCOMPLEXSCRIPTS
  260. if (pCF->_wScript && !_pme->GetPasswordChar())
  261. {
  262. CUniscribe* pusp = _pme->Getusp();
  263. Assert(pusp);
  264. const SCRIPT_PROPERTIES* psp = pusp->GeteProp(pCF->_wScript);
  265. plsrun->_a.eScript = pCF->_wScript < SCRIPT_MAX_COUNT ? pCF->_wScript : 0;
  266. plsrun->_a.fRTL = !psp->fNumeric && (IsBiDiCharRep(pCF->_iCharRep) || IsBiDiCharSet(psp->bCharSet));
  267. plsrun->_a.fLogicalOrder = TRUE;
  268. }
  269. #endif
  270. }
  271. return plsrun;
  272. }
  273. /*
  274. * COls::~COls()
  275. *
  276. * @mfunc
  277. * Destructor
  278. */
  279. COls::~COls()
  280. {
  281. for (int ichunk = 0, cchunk = _rglsrunChunk.Count(); ichunk < cchunk; ichunk++)
  282. delete []_rglsrunChunk.Elem(ichunk)->_prglsrun;
  283. DestroyLine(NULL);
  284. if (g_plsc)
  285. LsDestroyContext(g_plsc);
  286. }
  287. /*
  288. * COls::Init(pme)
  289. *
  290. * @mfunc
  291. * Initialize this LineServices object
  292. *
  293. * @rdesc
  294. * HRESULT = (success) ? NOERROR : E_FAIL
  295. */
  296. HRESULT COls::Init(
  297. CMeasurer *pme)
  298. {
  299. _pme = pme;
  300. if(g_plsc)
  301. return NOERROR;
  302. // Build LS context to create
  303. LSCONTEXTINFO lsctxinf;
  304. // Setup object handlers
  305. LSIMETHODS vlsctxinf[OBJID_COUNT];
  306. vlsctxinf[OBJID_OLE] = vlsimethodsOle;
  307. if(LsGetReverseLsimethods(&vlsctxinf[OBJID_REVERSE]) != lserrNone)
  308. return E_FAIL;
  309. lsctxinf.cInstalledHandlers = OBJID_COUNT;
  310. lsctxinf.pInstalledHandlers = &vlsctxinf[0];
  311. // Set default and all other characters to 0xFFFF
  312. memset(&lsctxinf.lstxtcfg, 0xFF, sizeof(lsctxinf.lstxtcfg));
  313. lsctxinf.fDontReleaseRuns = TRUE;
  314. lsctxinf.lstxtcfg.cEstimatedCharsPerLine = cchLineHint;
  315. // Set the characters we handle
  316. // FUTURE (keithcu) Support more characters in RE 4.0.
  317. lsctxinf.lstxtcfg.wchNull = 0;
  318. lsctxinf.lstxtcfg.wchSpace = ' ';
  319. lsctxinf.lstxtcfg.wchNonBreakSpace = NBSPACE;
  320. lsctxinf.lstxtcfg.wchNonBreakHyphen = NBHYPHEN;
  321. lsctxinf.lstxtcfg.wchNonReqHyphen = SOFTHYPHEN;
  322. lsctxinf.lstxtcfg.wchHyphen = '-';
  323. lsctxinf.lstxtcfg.wchEmDash = EMDASH;
  324. lsctxinf.lstxtcfg.wchEnDash = ENDASH;
  325. lsctxinf.lstxtcfg.wchEmSpace = EMSPACE;
  326. lsctxinf.lstxtcfg.wchEnSpace = ENSPACE;
  327. lsctxinf.lstxtcfg.wchTab = '\t';
  328. lsctxinf.lstxtcfg.wchEndLineInPara = '\v';
  329. lsctxinf.lstxtcfg.wchEndPara1 = '\r';
  330. lsctxinf.lstxtcfg.wchEndPara2 = '\n';
  331. lsctxinf.lstxtcfg.wchVisiAltEndPara =
  332. lsctxinf.lstxtcfg.wchVisiEndPara =
  333. lsctxinf.lstxtcfg.wchVisiEndLineInPara = ' ';
  334. // Auto number escape character
  335. lsctxinf.lstxtcfg.wchEscAnmRun = wchObjectEnd;
  336. lsctxinf.pols = this;
  337. lsctxinf.lscbk = lscbk;
  338. if(LsCreateContext(&lsctxinf, &g_plsc) != lserrNone)
  339. return E_FAIL;
  340. //REVIEW (keithcu) Quill seems to have a more mature kinsoku
  341. //table. For example, we don't allow breaking across space between
  342. //a word and the ending punctuation. French people want this behavior.
  343. BYTE rgbrkpairsKinsoku[cKinsokuCategories][cKinsokuCategories];
  344. BYTE *prgbrkpairsKinsoku = &rgbrkpairsKinsoku[0][0];
  345. LCID lcid = pme->GetCF()->_lcid;
  346. for(LONG i = 0; i < cKinsokuCategories; i++)
  347. {
  348. for(LONG j = 0; j < cKinsokuCategories; j++)
  349. {
  350. LONG iBreak = 2*CanBreak(i, j);
  351. // If don't break, allow break across blanks unless first
  352. // char is open brace or second char is close brace
  353. if (!iBreak &&
  354. GetKinsokuClass(i, 0xFFFF, lcid) != brkclsOpen &&
  355. GetKinsokuClass(j, 0xFFFF, lcid) != brkclsOpen)
  356. {
  357. iBreak = 1;
  358. }
  359. *prgbrkpairsKinsoku++ = iBreak;
  360. }
  361. }
  362. if(g_plsc->SetBreaking(ARRAY_SIZE(rglsbrkDefault), rglsbrkDefault,
  363. cKinsokuCategories, &rgbrkpairsKinsoku[0][0]) != lserrNone)
  364. {
  365. return E_FAIL;
  366. }
  367. return NOERROR;
  368. }
  369. /*
  370. * COls::QueryLineInfo(&lslinfo, pupStart, pdupWidth)
  371. *
  372. * @mfunc
  373. * Wrapper for LsQueryLineDup which is not called with full-justified
  374. * text because it's slow.
  375. */
  376. void COls::QueryLineInfo(
  377. LSLINFO &lslinfo,
  378. LONG * pupStart,
  379. LONG * pdupWidth)
  380. {
  381. const CParaFormat *pPF = _pme->Get_pPF();
  382. if (!lslinfo.fForcedBreak && /* lslinfo.endr <= endrHyphenated && */
  383. pPF->_bAlignment == PFA_FULL_INTERWORD && _pme->_pdp->GetWordWrap() &&
  384. pPF->_dxStartIndent == 0 && pPF->_dxOffset == 0 && pPF->_wNumbering == 0)
  385. {
  386. *pupStart = 0;
  387. *pdupWidth = _pme->LUtoDU(_pme->_dulLayout);
  388. }
  389. else
  390. {
  391. LONG upJunk, upStartTrailing;
  392. LsQueryLineDup(_plsline, &upJunk, &upJunk, pupStart, &upStartTrailing, &upJunk);
  393. *pdupWidth = upStartTrailing - *pupStart;
  394. }
  395. }
  396. /*
  397. * COls::MeasureLine(pliTarget)
  398. *
  399. * @mfunc
  400. * Wrapper for LsCreateLine
  401. *
  402. * @rdesc
  403. * TRUE if success, FALSE if failed
  404. */
  405. BOOL COls::MeasureLine(
  406. CLine * pliTarget) //@parm Returns target-device line metrics (optional)
  407. {
  408. CMeasurer *pme = _pme;
  409. const CDisplay *pdp = pme->_pdp;
  410. LONG cp = pme->GetCp();
  411. #ifdef DEBUG
  412. LONG cchText = pme->GetTextLength(); // For DEBUG...
  413. AssertSz(cp < cchText || !pme->IsRich() && cp == cchText, "COls::Measure: trying to measure past EOD");
  414. #endif
  415. DestroyLine(NULL);
  416. _cp = cp;
  417. _pdp = pdp;
  418. pme->SetUseTargetDevice(FALSE);
  419. LSDEVRES lsdevres;
  420. if (IsUVerticalTflow(pme->GetTflow()))
  421. {
  422. lsdevres.dxpInch = pme->_dvpInch;
  423. lsdevres.dypInch = pme->_dupInch;
  424. lsdevres.dxrInch = pme->_dvrInch;
  425. lsdevres.dyrInch = pme->_durInch;
  426. }
  427. else
  428. {
  429. lsdevres.dxpInch = pme->_dupInch;
  430. lsdevres.dypInch = pme->_dvpInch;
  431. lsdevres.dxrInch = pme->_durInch;
  432. lsdevres.dyrInch = pme->_dvrInch;
  433. }
  434. g_plsc->SetDoc(TRUE, lsdevres.dyrInch == lsdevres.dypInch &&
  435. lsdevres.dxrInch == lsdevres.dxpInch, &lsdevres);
  436. DWORD cBreakRecOut;
  437. LSLINFO lslinfo;
  438. BREAKREC rgBreak[MAX_OBJ_DEPTH];
  439. //If the first character on the line is a wrapping OLE object, add it to the
  440. //the layout queue.
  441. {
  442. COleObject *pobj = pme->GetObjectFromCp(cp);
  443. if(pobj && pobj->FWrapTextAround())
  444. pme->AddObjectToQueue(pobj);
  445. }
  446. LONG dulLayout = pme->_dulLayout;
  447. if(!pdp->GetWordWrap())
  448. {
  449. dulLayout = pme->DUtoLU(pdp->GetDupView());
  450. const LONG *pl = pme->GetPF()->GetTabs();
  451. if(pl && GetTabPos(*pl) > dulLayout)// Fix for Access big TAB 7963
  452. dulLayout *= 4; // dulLayout has to be larger
  453. } // than TAB
  454. dulLayout = max(dulLayout, 0);
  455. LSERR lserr = g_plsc->CreateLine(cp, dulLayout, NULL, 0, MAX_OBJ_DEPTH, rgBreak,
  456. &cBreakRecOut, &lslinfo, &_plsline);
  457. //Line Services doesn't put the autonumbering dimensions into the line,
  458. //so we have to do it ourselves.
  459. lslinfo.dvpAscent = max(lslinfo.dvpAscent, lslinfo.dvpAscentAutoNumber);
  460. lslinfo.dvpDescent = max(lslinfo.dvpDescent, lslinfo.dvpDescentAutoNumber);
  461. pme->SetUseTargetDevice(FALSE);
  462. lslinfo.cpLim = GetCpReFromCpLs(lslinfo.cpLim);
  463. if (lserr != lserrNone)
  464. {
  465. AssertSz(lserr == lserrOutOfMemory, "Line format failed for invalid reason");
  466. pme->GetPed()->GetCallMgr()->SetOutOfMemory();
  467. return FALSE;
  468. }
  469. if(!pme->IsRenderer())
  470. {
  471. // Save some LineServices results in the measurer's CLine
  472. pme->_li._cch = lslinfo.cpLim - cp;
  473. AssertSz(pme->_li._cch > 0, "no cps on line");
  474. LONG upStart, dupWidth;
  475. // Query for line width and indent.
  476. QueryLineInfo(lslinfo, &upStart, &dupWidth);
  477. pme->_li._upStart = upStart;
  478. pme->_li._dup = dupWidth;
  479. if(pme->IsRich())
  480. {
  481. pme->_li._dvpHeight = lslinfo.dvpAscent + lslinfo.dvpDescent;
  482. pme->_li._dvpDescent = lslinfo.dvpDescent;
  483. }
  484. else
  485. pme->CheckLineHeight(); // Use default heights
  486. pme->_li._cchEOP = 0;
  487. pme->SetCp(lslinfo.cpLim);
  488. if(pme->_rpTX.IsAfterEOP()) // Line ends with an EOP
  489. { // Store cch of EOP (1 or 2)
  490. pme->_rpTX.BackupCRLF(FALSE);
  491. UINT ch = pme->GetChar();
  492. if(ch == CR || pme->GetPed()->fUseCRLF() && ch == LF || ch == CELL)
  493. pme->_li._fHasEOP = TRUE;
  494. pme->_li._cchEOP = pme->_rpTX.AdvanceCRLF(FALSE);
  495. }
  496. if (lslinfo.cpLim > pme->GetTextLength() &&
  497. (!pme->IsRich() || pme->IsHidden()))
  498. {
  499. Assert(lslinfo.cpLim == pme->GetTextLength() + 1);
  500. pme->_li._cch--;
  501. }
  502. else
  503. pme->AdjustLineHeight();
  504. pme->UpdateWrapState(pme->_li._dvpHeight, pme->_li._dvpDescent);
  505. }
  506. //Setup pliTarget if caller requests it
  507. if (pliTarget)
  508. {
  509. CLine liSave = pme->_li;
  510. pme->_li._dvpHeight = max(lslinfo.dvrAscent, lslinfo.dvrAscentAutoNumber) +
  511. max(lslinfo.dvrDescent, lslinfo.dvrDescentAutoNumber);
  512. pme->_li._dvpDescent = lslinfo.dvrDescent;
  513. pme->SetUseTargetDevice(TRUE);
  514. pme->AdjustLineHeight();
  515. pme->SetUseTargetDevice(FALSE);
  516. *pliTarget = pme->_li;
  517. pme->_li = liSave;
  518. }
  519. return TRUE;
  520. }
  521. /*
  522. * COls::RenderLine(&li, fLastLine)
  523. *
  524. * @mfunc
  525. * Wrapper for LsDisplayLine
  526. *
  527. * @rdesc
  528. * TRUE if success, FALSE if failed
  529. */
  530. BOOL COls::RenderLine(
  531. CLine & li, //@parm Line to render
  532. BOOL fLastLine)
  533. {
  534. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "COls::RenderLine");
  535. LONG cp = _pme->GetCp();
  536. CRenderer *pre = GetRenderer();
  537. Assert(pre->_fRenderer);
  538. pre->SetNumber(li._bNumber);
  539. pre->NewLine(li);
  540. if(li._fCollapsed) // Line is collapsed in Outline mode
  541. {
  542. pre->Move(li._cch); // Bypass line
  543. return TRUE; // Let dispml continue with next line
  544. }
  545. CreateOrGetLine();
  546. if(!_plsline)
  547. return FALSE;
  548. pre->SetCp(cp); // Back to beginning of line
  549. pre->Check_pccs(FALSE);
  550. pre->_li._upStart = 0;
  551. Assert(pre->_fTarget == FALSE);
  552. LONG cpSelMin, cpSelMost;
  553. LONG dup, dvp;
  554. HDC hdcSave = pre->StartLine(li, fLastLine, cpSelMin, cpSelMost, dup, dvp);
  555. POINTUV pt = pre->GetCurPoint(); // Must follow offscreen setup
  556. POINT ptStart; // since _ptCur, _rc change
  557. RECTUV rcuv = pre->GetClipRect();
  558. pt.u += pre->XFromU(0);
  559. pt.v += li._dvpHeight - li._dvpDescent;
  560. memcpy(&ptStart, &pt, sizeof(ptStart));
  561. RECT rc;
  562. pre->GetPdp()->RectFromRectuv(rc, rcuv);
  563. LSERR lserr = LsDisplayLine(_plsline, &ptStart, pre->GetPdp()->IsMain() ? ETO_CLIPPED : 0, &rc);
  564. AssertSz(lserr == lserrNone, "COls::RenderLine: error in rendering line");
  565. pre->EndLine(hdcSave, dup, dvp);
  566. pre->SetCp(cp + li._cch);
  567. return lserr == lserrNone;
  568. }
  569. /*
  570. * COls::CreateOrGetLine()
  571. *
  572. * @mfunc
  573. * If _plsline is nonNull and _cp equals _pme->GetCp(), return. Else
  574. * create line with caching so that _plsline and _cp are correct for
  575. * current line
  576. */
  577. void COls::CreateOrGetLine()
  578. {
  579. if(_plsline && _pme->GetCp() == _cp && _pme->_pdp == _pdp)
  580. return;
  581. MeasureLine(NULL); // Define new _plsline
  582. }
  583. /*
  584. * COls::MeasureText(cch, taMode, pdispdim)
  585. *
  586. * @mfunc
  587. * Gets x offset to cp given by CMeasurer _pme + cch chars along with
  588. * display dimensions.
  589. *
  590. * @rdesc
  591. * xwidth measured
  592. */
  593. LONG COls::MeasureText(
  594. LONG cch, //(IN): Max cch to measure
  595. UINT taMode, //(IN): requested coordinate
  596. CDispDim *pdispdim) //(OUT): display dimensions
  597. {
  598. CMeasurer * pme = _pme;
  599. LONG cp = pme->GetCp() + cch; // Enter with me at start of line
  600. POINT pt; // Point at cp in client coords
  601. BOOL fAtLogicalRightEdge = FALSE;
  602. CreateOrGetLine();
  603. if(!_plsline)
  604. return 0;
  605. Assert(pme->_fTarget == FALSE);
  606. // Query point from cp
  607. DWORD cActualDepth;
  608. LSQSUBINFO lsqSubInfo[MAX_OBJ_DEPTH];
  609. LSTEXTCELL lsTextCell;
  610. memset(&lsTextCell, 0, sizeof(lsTextCell));
  611. LsQueryLineCpPpoint(_plsline, GetCpLsFromCpRe(cp), MAX_OBJ_DEPTH, &lsqSubInfo[0],
  612. &cActualDepth, &lsTextCell);
  613. pdispdim->lstflow = lsqSubInfo[cActualDepth - 1].lstflowSubline;
  614. pdispdim->dup = lsTextCell.dupCell;
  615. LSTFLOW lstflowLine = lsqSubInfo[0].lstflowSubline;
  616. POINT ptStart = {pme->XFromU(0), pme->_li._dvpHeight - pme->_li._dvpDescent};
  617. POINTUV ptuv = lsTextCell.pointUvStartCell;
  618. if(taMode & (TA_STARTOFLINE | TA_ENDOFLINE) && cActualDepth > 1)
  619. {
  620. ptuv = lsqSubInfo[0].pointUvStartRun;
  621. if(taMode & TA_ENDOFLINE)
  622. ptuv.u += lsqSubInfo[0].dupRun;
  623. }
  624. //If they ask for position inside ligature or at lim of line, give right edge of cell
  625. else if (cp > GetCpReFromCpLs(lsTextCell.cpStartCell))
  626. {
  627. fAtLogicalRightEdge = TRUE;
  628. if (lstflowLine != pdispdim->lstflow)
  629. ptuv.u -= lsTextCell.dupCell;
  630. else
  631. ptuv.u += lsTextCell.dupCell;
  632. }
  633. LsPointXYFromPointUV(&ptStart, lstflowLine, &ptuv, &pt);
  634. if (pdispdim->lstflow == lstflowWS && !(taMode & (TA_LOGICAL | TA_STARTOFLINE)))
  635. {
  636. if (fAtLogicalRightEdge)
  637. return pt.x;
  638. else
  639. pt.x -= pdispdim->dup - 1;
  640. }
  641. if (fAtLogicalRightEdge)
  642. pdispdim->dup = 0;
  643. return pt.x;
  644. }
  645. /*
  646. * COls::CchFromUp(pt, pdispdim, pcpActual)
  647. *
  648. * @mfunc
  649. * Moves _pme to pt.x. Calls LsQueryLinePointPcp()
  650. */
  651. void COls::CchFromUp(
  652. POINTUV pt, //@parm Point to find cch for in line
  653. CDispDim *pdispdim, //@parm dimensions of object
  654. LONG *pcpActual) //@parm CP point
  655. {
  656. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "COls::CchFromUp");
  657. // Make point relative to LS coordinate system - (0,0) in LS is at the
  658. // baseline of the line.
  659. POINTUV ptuv = {_pme->UFromX(pt.u), -pt.v + _pme->_li._dvpHeight - _pme->_li._dvpDescent};
  660. LONG cpStart = _pme->GetCp();
  661. CreateOrGetLine();
  662. if(!_plsline)
  663. return;
  664. Assert(_pme->_fTarget == FALSE);
  665. DWORD cActualDepth;
  666. LSQSUBINFO lsqSubInfo[MAX_OBJ_DEPTH];
  667. LSTEXTCELL lsTextCell;
  668. memset(&lsTextCell, 0, sizeof(lsTextCell));
  669. LsQueryLinePointPcp(_plsline, &ptuv, MAX_OBJ_DEPTH, &lsqSubInfo[0], &cActualDepth, &lsTextCell);
  670. if (cActualDepth == 0) //If we got back empty textcell, let's just query cp explicitly to get information
  671. {
  672. LsQueryLineCpPpoint(_plsline, cpStart, MAX_OBJ_DEPTH, &lsqSubInfo[0], &cActualDepth, &lsTextCell);
  673. Assert(cActualDepth != 0);
  674. }
  675. pdispdim->dup = lsTextCell.dupCell;
  676. pdispdim->lstflow = lsqSubInfo[cActualDepth - 1].lstflowSubline;
  677. LONG cp = GetCpReFromCpLs(lsTextCell.cpStartCell);
  678. // The queries above can fail in BiDi hidden text. Would be best to suppress
  679. // BiDi itemization in hidden text, but for now, here's a simple patch
  680. if(!cp)
  681. cp = cpStart;
  682. Assert(cp >= cpStart);
  683. *pcpActual = cp;
  684. POINTUV ptuvCell;
  685. //Convert the hit-test point from u,v of line to u,v of cell
  686. LsPointUV2FromPointUV1(lsqSubInfo[0].lstflowSubline, &lsTextCell.pointUvStartCell, &ptuv,
  687. lsqSubInfo[cActualDepth - 1].lstflowSubline, &ptuvCell);
  688. _pme->SetCp(cp);
  689. if (ptuvCell.u > lsTextCell.dupCell/2 ||
  690. ptuvCell.u > W32->GetDupSystemFont()/2 && _pme->GetChar() == WCH_EMBEDDING)
  691. {
  692. cp += lsTextCell.cpEndCell - lsTextCell.cpStartCell + 1;
  693. }
  694. #if !defined(NOCOMPLEXSCRIPTS)
  695. if (_pme->GetPed()->_pbrk)
  696. {
  697. // If text breaker is up, verify cluster before placing the caret
  698. CTxtBreaker* pbrk = _pme->GetPed()->_pbrk;
  699. LONG cpEnd = _pme->GetPed()->GetTextLength();
  700. while (cp < cpEnd && !pbrk->CanBreakCp(BRK_CLUSTER, cp))
  701. cp++;
  702. }
  703. #endif
  704. _pme->_li._cch = cp - _cp;
  705. Assert(_pme->_li._cch >= 0);
  706. _pme->SetCp(cp);
  707. }
  708. /*
  709. * COls::DestroyLine(pdp)
  710. *
  711. * @mfunc
  712. * Destroys any line data structures.
  713. */
  714. void COls::DestroyLine(CDisplay *pdp)
  715. {
  716. CLock lock;
  717. if (pdp && pdp != _pdp)
  718. return;
  719. if(_plsline)
  720. {
  721. g_plsc->DestroyLine(_plsline);
  722. _plsline = NULL;
  723. }
  724. if (_rgcp.Count())
  725. _rgcp.Clear(AF_KEEPMEM);
  726. int cchunk = _rglsrunChunk.Count();
  727. for (int ichunk = 0; ichunk < cchunk; ichunk++)
  728. _rglsrunChunk.Elem(ichunk)->_cel = 0;
  729. }
  730. /*
  731. * LimitChunk(pch, &cchChunk, f10Mode)
  732. *
  733. * @func
  734. * Return object ID at *pch and shorten cchChunk to 1 if object isn't
  735. * text and to the count of text chars up to a nontext object if one
  736. * occurs within cchChunk and within the current paragraph.
  737. *
  738. * @rdesc
  739. * Object ID at *pch
  740. */
  741. DWORD LimitChunk(
  742. const WCHAR *pch,
  743. LONG & cchChunk,
  744. BOOL f10Mode)
  745. {
  746. for(LONG i = 0; i < cchChunk && *pch != CR; i++, pch++)
  747. {
  748. switch(*pch)
  749. {
  750. case WCH_EMBEDDING:
  751. if(i == 0)
  752. {
  753. cchChunk = 1;
  754. return OBJID_OLE; // Entered at an OLE object
  755. }
  756. cchChunk = i; // Will break before
  757. break;
  758. case EURO:
  759. if (i == 0)
  760. {
  761. for(; i < cchChunk && *pch == EURO; i++)
  762. pch++;
  763. }
  764. cchChunk = i;
  765. break;
  766. case FF:
  767. if(f10Mode) // RichEdit 1.0 treats FFs as
  768. continue; // ordinary characters
  769. case CELL:
  770. cchChunk = i; // Will break before
  771. break;
  772. }
  773. }
  774. return idObjTextChp;
  775. }
  776. /*
  777. * COls::SetLsChp(dwObjId, plsrun, plsChp)
  778. *
  779. * @mfunc
  780. * Helper function that initializes an LS chp from RE CCharFormat
  781. *
  782. * @rdesc
  783. * TRUE iff IsHidden()
  784. */
  785. BOOL COls::SetLsChp(
  786. DWORD dwObjId, //(IN): Object id
  787. PLSRUN plsrun, //(IN): Current Run
  788. PLSCHP plsChp) //(OUT): LS chp
  789. {
  790. ZeroMemory(plsChp, sizeof(*plsChp));
  791. plsChp->idObj = (WORD)dwObjId;
  792. #ifndef NOCOMPLEXSCRIPTS
  793. if (_pme->GetPed()->IsComplexScript() && plsrun->_a.eScript && !plsrun->IsBullet())
  794. {
  795. CUniscribe* pusp = _pme->Getusp();
  796. Assert (pusp);
  797. const SCRIPT_PROPERTIES *psp = pusp->GeteProp(plsrun->_a.eScript);
  798. if (psp->fComplex || plsrun->_a.fRTL ||
  799. psp->fNumeric && W32->GetDigitSubstitutionMode() != DIGITS_NOTIMPL)
  800. {
  801. // 1. Complex script
  802. // 2. RTL (internal direction) run (handle mirror glyph i.e.'?')
  803. // 3. Numeric run and substitution mode is either Native or Context
  804. plsChp->fGlyphBased = TRUE;
  805. }
  806. }
  807. #endif
  808. const CCharFormat *pCF = plsrun->_pCF;
  809. DWORD dwEffects = pCF->_dwEffects;
  810. const CDisplay *pdp = _pme->_pdp;
  811. HDC hdc = pdp->GetDC();
  812. if((dwEffects & (CFE_UNDERLINE | CFE_LINK | CFE_REVISED)) ||
  813. GetDeviceCaps(hdc, TECHNOLOGY) == DT_RASDISPLAY && GetTmpUnderline(pCF->_sTmpDisplayAttrIdx))
  814. plsChp->fUnderline = TRUE;
  815. pdp->ReleaseDC(hdc);
  816. if(dwEffects & CFE_STRIKEOUT && !plsrun->IsBullet())
  817. plsChp->fStrike = TRUE;
  818. if (pCF->_yOffset || dwEffects & (CFE_SUPERSCRIPT | CFE_SUBSCRIPT))
  819. {
  820. _pme->SetUseTargetDevice(FALSE);
  821. CCcs *pccs = _pme->Check_pccs(plsrun->IsBullet());
  822. LONG yOffset, yAdjust;
  823. pccs->GetOffset(pCF, _pme->_dvpInch, &yOffset, &yAdjust);
  824. plsChp->dvpPos += yOffset + yAdjust;
  825. }
  826. if (pCF->CanKern() && !plsChp->fGlyphBased)
  827. {
  828. CKernCache *pkc = fc().GetKernCache(pCF->_iFont, pCF->_wWeight, pCF->_dwEffects & CFE_ITALIC);
  829. CCcs *pccs = _pme->Check_pccs(plsrun->IsBullet());
  830. if (pkc && pkc->FInit(pccs->_hfont))
  831. {
  832. plsChp->fApplyKern = TRUE;
  833. plsChp->dcpMaxContext = max(plsChp->dcpMaxContext, 2);
  834. }
  835. }
  836. //If its an OLE object, but the Object doesn't exist yet, then hide it
  837. if (dwObjId == OBJID_OLE)
  838. {
  839. COleObject *pobj = _pme->GetObjectFromCp(_pme->GetCp());
  840. if (!pobj)
  841. return TRUE; //FUTURE (keithcu) Remove the need for this.
  842. if (pobj->FWrapTextAround())
  843. {
  844. _pme->AddObjectToQueue(pobj);
  845. return TRUE;
  846. }
  847. }
  848. return dwEffects & CFE_HIDDEN;
  849. }
  850. /*
  851. * COls::FetchAnmRun(cp, plpwchRun, pcchRun, pfHidden, plsChp, pplsrun)
  852. *
  853. * @mfunc
  854. * LineServices fetch bullets/numbering callback
  855. *
  856. * @rdesc
  857. * LSERR
  858. */
  859. LSERR WINAPI COls::FetchAnmRun(
  860. LSCP cp, //@parm [IN]: RE cp
  861. LPCWSTR *plpwchRun, //@parm [OUT]: Run of characters
  862. DWORD * pcchRun, //@parm [OUT]: Count of characters in run
  863. BOOL * pfHidden, //@parm [OUT]: fHidden run?
  864. PLSCHP plsChp, //@parm [OUT]: Char properties of run
  865. PLSRUN * pplsrun) //@parm [OUT]: Abstract representation of run properties
  866. {
  867. if (cp == cpFirstAnm && _pme->Get_pPF()->IsRtl())
  868. {
  869. ZeroMemory(plsChp, sizeof(*plsChp));
  870. plsChp->idObj = OBJID_REVERSE;
  871. *pfHidden = 0; *pcchRun = 1;
  872. *pplsrun = GetPlsrun(_pme->GetCp(), &_CFBullet, TRUE);
  873. *plpwchRun = &_szAnm[0];
  874. return lserrNone;
  875. }
  876. *plpwchRun = &_szAnm[cp - cpFirstAnm];
  877. *pcchRun = _cchAnm - (cp - cpFirstAnm);
  878. *pplsrun = GetPlsrun(_pme->GetCp(), &_CFBullet, TRUE);
  879. SetLsChp(idObjTextChp, *pplsrun, plsChp);
  880. *pfHidden = FALSE;
  881. if (!_pme->GetNumber())
  882. plsChp->fUnderline = FALSE;
  883. return lserrNone;
  884. }
  885. /*
  886. * OlsFetchRun(pols, cpLs, plpwchRun, pcchRun, pfHidden, plsChp, pplsrun)
  887. *
  888. * @func
  889. * LineServices fetch-run callback
  890. *
  891. * @rdesc
  892. * LSERR
  893. */
  894. LSERR WINAPI OlsFetchRun(
  895. POLS pols, //@parm [IN]: COls *
  896. LSCP cpLs, //@parm [IN]: LS cp
  897. LPCWSTR *plpwchRun, //@parm [OUT]: Run of characters
  898. DWORD * pcchRun, //@parm [OUT]: Count of characters in run
  899. BOOL * pfHidden, //@parm [OUT]: Hidden run?
  900. PLSCHP plsChp, //@parm [OUT]: Char properties of run
  901. PLSRUN * pplsrun) //@parm [OUT]: Abstract representation of run properties
  902. {
  903. if(cpLs < 0)
  904. return pols->FetchAnmRun(cpLs, plpwchRun, pcchRun, pfHidden, plsChp, pplsrun);
  905. CMeasurer *pme = pols->GetMeasurer();
  906. CTxtEdit *ped = pme->GetPed();
  907. WCHAR chPassword = pme->GetPasswordChar();
  908. LONG cpAccelerator = ped->GetCpAccelerator();
  909. BOOL fAccelerator = FALSE;
  910. BOOL f10Mode = ped->Get10Mode();
  911. if (cpLs == pols->_cp)
  912. {
  913. // If we are formatting (or re-formatting) the line, cleanup
  914. if (pols->_rgcp.Count())
  915. pols->_rgcp.Clear(AF_KEEPMEM);
  916. pols->_cEmit = 0;
  917. }
  918. long cpRe = pols->GetCpReFromCpLs(cpLs);
  919. pme->SetCp(cpRe); // Start fetching at given cp
  920. #ifndef NOCOMPLEXSCRIPTS
  921. BOOL fFetchBraces = ped->IsBiDi() && g_pusp && g_pusp->IsValid() &&
  922. !ped->_fItemizePending && ped->GetAdjustedTextLength();
  923. BOOL fStart = FALSE;
  924. if (fFetchBraces && pme->_rpCF.IsValid())
  925. {
  926. // Consider emitting braces only at the run boundary or start of a fetched line
  927. if (cpRe == pols->_cp || !pme->GetIchRunCF() || !pme->GetCchLeftRunCF())
  928. {
  929. SHORT cBrClose, cBrOpen;
  930. BYTE bBaseLevel = pme->IsParaRTL() ? 1 : 0;
  931. BYTE bLevel = bBaseLevel;
  932. BYTE bLevelPrev = bBaseLevel; // Assume base level
  933. if (cpRe < ped->GetTextLength())
  934. {
  935. CBiDiLevel level;
  936. bLevel = pme->_rpCF.GetLevel(&level); // Got level of current run
  937. fStart = level._fStart;
  938. }
  939. if (cpRe > pols->_cp && pme->Move(-1))
  940. {
  941. if (pme->_rpPF.SameLevel(bBaseLevel)) // Preceding run may be hidden
  942. bLevelPrev = pme->_rpCF.GetLevel(); // Got level of preceding run
  943. pme->Move(1); // Resume position
  944. }
  945. cBrOpen = cBrClose = bLevel - bLevelPrev;
  946. if (fStart) // Start embedding at current run
  947. cBrClose = bBaseLevel - bLevelPrev; // This means we must close all braces of preceding run
  948. cBrClose = max(0, -cBrClose);
  949. if (cBrClose > 0 && pols->BracesBeforeCp(cpLs) < cBrClose)
  950. {
  951. // Emit close braces
  952. if (pols->_cEmit > 0)
  953. {
  954. EmitBrace(pols, plsChp, pfHidden, pcchRun, pplsrun, plpwchRun, idObjTextChp, rgchObjectEnd);
  955. if (pols->AddBraceCp(cpLs))
  956. pols->_cEmit--;
  957. #ifdef DEBUG_BRACE
  958. Tracef(TRCSEVNONE, "CLOSE(%d) cpLs %d: emitted %d", cBrClose, cpLs, pols->_cEmit);
  959. #endif
  960. return lserrNone;
  961. }
  962. else
  963. {
  964. // We assert. You can click "Ignore All" with no hang.
  965. AssertSz(FALSE, "Prevent emitting close brace (no open counterpart)");
  966. }
  967. }
  968. if (fStart) // start embedding at the current run
  969. cBrOpen = bLevel - bBaseLevel; // we begin openning braces
  970. if (cBrOpen > 0 && pols->BracesBeforeCp(cpLs) < cBrOpen + cBrClose)
  971. {
  972. // Emit open braces
  973. EmitBrace(pols, plsChp, pfHidden, pcchRun, pplsrun, plpwchRun, OBJID_REVERSE, L" ");
  974. if (pols->AddBraceCp(cpLs))
  975. pols->_cEmit++;
  976. #ifdef DEBUG_BRACE
  977. Tracef(TRCSEVNONE, "OPEN(%d) cpLs %d: emitted %d", cBrOpen, cpLs, pols->_cEmit);
  978. #endif
  979. return lserrNone;
  980. }
  981. }
  982. }
  983. #endif
  984. // Done fetching braces.
  985. // Begin getting real data...
  986. #ifdef DEBUG_BRACE
  987. Tracef(TRCSEVNONE, "cpLs %d: emitted %d", cpLs, pols->_cEmit);
  988. #endif
  989. // Initialized chunk to count of characters in format run
  990. LONG cchChunk = pme->GetCchLeftRunCF();
  991. DWORD dwObjId = idObjTextChp;
  992. const CCharFormat *pCF = pme->GetCF();
  993. if(!pme->IsHidden()) // Run isn't hidden
  994. {
  995. LONG cch;
  996. WCHAR ch;
  997. *plpwchRun = pme->GetPch(cch); // Get text in run
  998. if(cch && **plpwchRun == NOTACHAR)
  999. {
  1000. (*plpwchRun)++; // Bypass NOTACHAR
  1001. cch--;
  1002. }
  1003. cchChunk = min(cchChunk, cch); // Maybe less than cchChunk
  1004. if (!pme->GetPdp()->IsMetafile())
  1005. cchChunk = min(cchChunk, 255); // Prevent us from having runs which are TOO long
  1006. // Support khyphChangeAfter
  1007. if (pols->_cp == cpRe && pme->GetIhyphPrev())
  1008. {
  1009. UINT khyph;
  1010. CHyphCache *phc = ped->GetHyphCache();
  1011. Assert(phc);
  1012. phc->GetAt(pme->GetIhyphPrev(), khyph, ch);
  1013. if (khyph == khyphChangeAfter)
  1014. {
  1015. pols->_rgchTemp[0] = ch;
  1016. *plpwchRun = pols->_rgchTemp;
  1017. cchChunk = 1;
  1018. }
  1019. }
  1020. if (chPassword)
  1021. {
  1022. cchChunk = min(cchChunk, (int)ARRAY_SIZE(pols->_rgchTemp));
  1023. for (int i = 0; i < cchChunk; i++)
  1024. {
  1025. ch = (*plpwchRun)[i];
  1026. if(IN_RANGE(0xDC00, ch, 0xDFFF))
  1027. { // Surrogate trail word
  1028. if(i) // Truncate current run at
  1029. cchChunk = i; // trail word
  1030. else // Make trail word a hidden
  1031. { // single-char run
  1032. *pplsrun = pols->GetPlsrun(cpRe, pCF, FALSE);
  1033. pols->SetLsChp(dwObjId, *pplsrun, plsChp);
  1034. *pfHidden = TRUE;
  1035. *pcchRun = 1;
  1036. return lserrNone;
  1037. }
  1038. }
  1039. else
  1040. pols->_rgchTemp[i] = IsEOP(ch) ? ch : chPassword;
  1041. }
  1042. *plpwchRun = pols->_rgchTemp;
  1043. }
  1044. if(cpAccelerator != -1)
  1045. {
  1046. LONG cpCur = pme->GetCp(); // Get current cp
  1047. // Does accelerator character fall in this chunk?
  1048. if (cpCur < cpAccelerator &&
  1049. cpCur + cchChunk > cpAccelerator)
  1050. {
  1051. // Yes. Reduce chunk to char just before accelerator
  1052. cchChunk = cpAccelerator - cpCur;
  1053. }
  1054. // Is this character the accelerator?
  1055. else if(cpCur == cpAccelerator)
  1056. { // Set chunk size to 1 since only
  1057. cchChunk = 1; // want to output underlined char
  1058. fAccelerator = TRUE; // Tell downstream routines that
  1059. // we're handling accelerator
  1060. }
  1061. }
  1062. if(pCF->_dwEffects & CFE_ALLCAPS)
  1063. {
  1064. cchChunk = min(cchChunk, (int)ARRAY_SIZE(pols->_rgchTemp));
  1065. memcpy(pols->_rgchTemp, *plpwchRun, cchChunk * sizeof(WCHAR));
  1066. CharUpperBuff(pols->_rgchTemp, cchChunk);
  1067. *plpwchRun = pols->_rgchTemp;
  1068. }
  1069. //Line Services handles page breaks in a weird way, so lets just convert to a CR.
  1070. if (*plpwchRun && (*(*plpwchRun) == FF && !f10Mode || *(*plpwchRun) == CELL))
  1071. {
  1072. pols->_szAnm[0] = CR;
  1073. *plpwchRun = pols->_szAnm;
  1074. cchChunk = 1;
  1075. }
  1076. AssertSz(cpRe < ped->GetTextLength() || !ped->IsRich(), "0-length run at end of control");
  1077. AssertSz(cch || !ped->IsRich(), "0-length run at end of control");
  1078. // Set run size appropriately for any objects that are in run
  1079. dwObjId = LimitChunk(*plpwchRun, cchChunk, f10Mode);
  1080. // Get regular highlighted positions
  1081. LONG cpSelMin, cpSelMost;
  1082. ped->GetSelRangeForRender(&cpSelMin, &cpSelMost);
  1083. if(cpSelMin != cpSelMost)
  1084. {
  1085. if(cpRe >= cpSelMin)
  1086. {
  1087. if(cpRe < cpSelMost)
  1088. {
  1089. // Current text falls inside selection
  1090. cch = cpSelMost - cpRe;
  1091. cchChunk = min(cchChunk, cch);
  1092. }
  1093. }
  1094. else if(cpRe + cchChunk >= cpSelMin)
  1095. {
  1096. // cp < cpSelMin - run starts outside of selection.
  1097. // Limit text to start of selection.
  1098. cchChunk = cpSelMin - cpRe;
  1099. }
  1100. }
  1101. }
  1102. *pplsrun = pols->GetPlsrun(cpRe, pCF, FALSE);
  1103. *pfHidden = pols->SetLsChp(dwObjId, *pplsrun, plsChp);
  1104. if (fAccelerator)
  1105. plsChp->fUnderline = TRUE;
  1106. if(!cchChunk) // Happens in plain-text controls
  1107. { // and if hidden text to end of story
  1108. if (!ped->IsRich() && pols->_cEmit > 0)
  1109. {
  1110. EmitBrace(pols, plsChp, pfHidden, pcchRun, pplsrun, plpwchRun, idObjTextChp, rgchObjectEnd);
  1111. TRACEWARNSZ("(plain)Auto-emit a close brace to make balance");
  1112. if (pols->AddBraceCp(cpLs))
  1113. pols->_cEmit--;
  1114. return lserrNone;
  1115. }
  1116. cchChunk = 1;
  1117. *plpwchRun = szCR;
  1118. *pfHidden = FALSE;
  1119. #ifndef NOCOMPLEXSCRIPTS
  1120. //Paragraph marks should not have any script state associated with them,
  1121. //even if the pCF that point to does.
  1122. ZeroMemory(&(*pplsrun)->_a, sizeof((*pplsrun)->_a));
  1123. #endif
  1124. }
  1125. *pcchRun = cchChunk;
  1126. return lserrNone;
  1127. }
  1128. /*
  1129. * OlsGetAutoNumberInfo (pols, plskalAnm, plschpAnm, pplsrunAnm, pwchAdd, plschp,
  1130. * pplsrun, pfWord95Model, pduaSpaceAnm, pduaWidthAnm)
  1131. * @func
  1132. * LineServices fetch autonumbering info callback. Return info needed
  1133. * by LS for auto numbering. Get the chp/run for last char from auto
  1134. * number run. Always say we are Word95 model Anm and get rest of info
  1135. * from paragraph properties.
  1136. *
  1137. * @rdesc
  1138. * LSERR
  1139. */
  1140. LSERR WINAPI OlsGetAutoNumberInfo(
  1141. POLS pols, //(IN): Client context
  1142. LSKALIGN *plskalAnm, //(OUT):Justification
  1143. PLSCHP plschpAnm,
  1144. PLSRUN *pplsrunAnm,
  1145. WCHAR * pwchAdd, //(OUT):char to add (Nil is treated as none)
  1146. PLSCHP plsChp, //(OUT):chp for bridge character
  1147. PLSRUN * pplsrun, //(OUT):Run for bridge character
  1148. BOOL * pfWord95Model, //(OUT):Type of autonumber run
  1149. long * pduaSpaceAnm, //(OUT):Relevant iff fWord95Model
  1150. long * pduaWidthAnm) //(OUT):Relevant iff fWord95Model
  1151. {
  1152. CMeasurer *pme = pols->GetMeasurer();
  1153. const CParaFormat *pPF = pme->Get_pPF();
  1154. *pplsrunAnm = *pplsrun = pols->GetPlsrun(pme->GetCp(), &pols->_CFBullet, TRUE);
  1155. pols->SetLsChp(idObjTextChp, *pplsrun, plsChp);
  1156. if (!pme->GetNumber())
  1157. plsChp->fUnderline = FALSE;
  1158. *plschpAnm = *plsChp;
  1159. *pwchAdd = '\t';
  1160. *pfWord95Model = TRUE;
  1161. *pduaSpaceAnm = 0;
  1162. *pduaWidthAnm = pPF->_wNumberingTab ? pPF->_wNumberingTab : pPF->_dxOffset;
  1163. LONG Alignment = pPF->_wNumberingStyle & 3;
  1164. *plskalAnm = (LSKALIGN)(lskalLeft + Alignment);
  1165. if(Alignment != tomAlignLeft)
  1166. {
  1167. if(Alignment == tomAlignRight)
  1168. {
  1169. *pduaSpaceAnm = *pduaWidthAnm; // End at pPF->_dxStartIndent
  1170. *pduaWidthAnm += pPF->_dxStartIndent;
  1171. }
  1172. else
  1173. {
  1174. Assert(Alignment == tomAlignCenter);
  1175. *pduaWidthAnm *= 2; // Center at pPF->dxStartIndent
  1176. }
  1177. }
  1178. return lserrNone;
  1179. }
  1180. /*
  1181. * OlsGetNumericSeparators (pols, plsrun, pwchDecimal, pwchThousands)
  1182. *
  1183. * @func
  1184. * Get numeric separators needed, e.g., for decimal tabs
  1185. *
  1186. * @rdesc
  1187. * LSERR
  1188. */
  1189. LSERR WINAPI OlsGetNumericSeparators(
  1190. POLS pols, //(IN): pols
  1191. PLSRUN plsrun, //(IN): Run (cp here)
  1192. WCHAR * pwchDecimal, //(OUT): Decimal separator for this run
  1193. WCHAR * pwchThousands) //(OUT): Thousands separator for this run
  1194. {
  1195. LCID lcid = plsrun->_pCF->_lcid;
  1196. WCHAR ch = TEXT('.');
  1197. // This may need to be virtualized for Win95/CE...
  1198. ::GetLocaleInfo(lcid, LOCALE_SDECIMAL, &ch, 1);
  1199. *pwchDecimal = ch;
  1200. ch = TEXT(',');
  1201. ::GetLocaleInfo(lcid, LOCALE_STHOUSAND, &ch, 1);
  1202. *pwchThousands = ch;
  1203. return lserrNone;
  1204. }
  1205. /*
  1206. * OlsFetchPap (pols, cpLS, plspap)
  1207. *
  1208. * @func
  1209. * Fetch paragraph properties
  1210. *
  1211. * @rdesc
  1212. * LSERR
  1213. */
  1214. LSERR WINAPI OlsFetchPap(
  1215. POLS pols, //(IN): pols
  1216. LSCP cpLs, //(IN): an arbitrary cp value inside paragraph
  1217. PLSPAP plspap) //(OUT): Paragraph properties.
  1218. {
  1219. CMeasurer *pme = pols->GetMeasurer();
  1220. pme->SetCp(pols->_cp);
  1221. LONG dvpJunk;
  1222. const CParaFormat *pPF = pme->Get_pPF();
  1223. CTxtEdit * ped = pme->GetPed();
  1224. // Default all results to 0
  1225. ZeroMemory(plspap, sizeof(*plspap));
  1226. //LS doesn't really care where the paragraph starts
  1227. plspap->cpFirst = pols->_cp;
  1228. if(plspap->cpFirst && !pme->fFirstInPara()) // Not first in para: say para
  1229. plspap->cpFirst--; // starts one char earlier
  1230. plspap->cpFirstContent = plspap->cpFirst;
  1231. if (pPF->IsRtl())
  1232. plspap->lstflow = lstflowWS;
  1233. // Alignment
  1234. plspap->lskal = (LSKALIGN) g_rgREtoTOMAlign[pPF->_bAlignment];
  1235. if (pPF->IsRtl())
  1236. { //For Line Services, left means near and right means far.
  1237. if (plspap->lskal == lskalLeft)
  1238. plspap->lskal = lskalRight;
  1239. else if (plspap->lskal == lskalRight)
  1240. plspap->lskal = lskalLeft;
  1241. }
  1242. if (pPF->_bAlignment == PFA_FULL_INTERWORD)
  1243. {
  1244. plspap->lskal = lskalLeft;
  1245. plspap->lskj = lskjFullInterWord;
  1246. }
  1247. //Enable hyphenation?
  1248. if (ped->_pfnHyphenate && !(pPF->_wEffects & PFE_DONOTHYPHEN))
  1249. {
  1250. plspap->grpf |= fFmiDoHyphenation;
  1251. plspap->duaHyphenationZone = ped->_dulHyphenateZone;
  1252. }
  1253. // Kind of EOP
  1254. CTxtPtr tp(pme->_rpTX);
  1255. LONG results;
  1256. tp.FindEOP(tomForward, &results);
  1257. plspap->lskeop = (results & 3) == 2 ? lskeopEndPara12 : lskeopEndPara1;
  1258. // Line breaking
  1259. if (pPF->_bAlignment > PFA_FULL_INTERWORD || !ped->fUseSimpleLineBreak() ||
  1260. !pme->GetPdp()->GetWordWrap()) // No word wrap
  1261. {
  1262. plspap->grpf |= fFmiApplyBreakingRules | fFmiTreatHyphenAsRegular;
  1263. }
  1264. LONG dul = pPF->_dxRightIndent;
  1265. if (pme->IsMeasure())
  1266. {
  1267. COleObject *pobj = pme->FindFirstWrapObj(FALSE);
  1268. if (pobj && pobj->GetCp() <= pols->_cp)
  1269. {
  1270. LONG dulRight;
  1271. pobj->MeasureObj(1440, 1440, dulRight, dvpJunk, dvpJunk, 0, pme->GetTflow());
  1272. dul = max(dul, dulRight);
  1273. pme->_li._cObjectWrapRight = pme->CountQueueEntries(FALSE);
  1274. }
  1275. }
  1276. else if (pme->_li._cObjectWrapRight)
  1277. {
  1278. LONG cpObj = pme->FindCpDraw(pme->GetCp() + 1, pme->_li._cObjectWrapRight, FALSE);
  1279. COleObject *pobj = pme->GetObjectFromCp(cpObj);
  1280. LONG dulRight;
  1281. pobj->MeasureObj(1440, 1440, dulRight, dvpJunk, dvpJunk, 0, pme->GetTflow());
  1282. dul = max(dul, dulRight);
  1283. }
  1284. plspap->uaRightBreak = dul;
  1285. plspap->uaRightJustify = dul;
  1286. pme->_upRight = pme->LUtoDU(dul);
  1287. if (!pme->_pdp->GetWordWrap())
  1288. plspap->uaRightBreak = uLsInfiniteRM;
  1289. if(ped->IsInOutlineView())
  1290. {
  1291. plspap->uaLeft = lDefaultTab/2 * (pPF->_bOutlineLevel + 1);
  1292. plspap->duaIndent = 0;
  1293. }
  1294. else
  1295. {
  1296. LONG dulPicture = 0;
  1297. if (pme->IsMeasure())
  1298. {
  1299. COleObject *pobj = pme->FindFirstWrapObj(TRUE);
  1300. if (pobj && pobj->GetCp() <= pols->_cp)
  1301. {
  1302. pobj->MeasureObj(1440, 1440, dulPicture, dvpJunk, dvpJunk, 0, pme->GetTflow());
  1303. pme->_li._cObjectWrapLeft = pme->CountQueueEntries(TRUE);
  1304. }
  1305. }
  1306. else if (pme->_li._cObjectWrapLeft)
  1307. {
  1308. LONG cpObj = pme->FindCpDraw(pme->GetCp() + 1, pme->_li._cObjectWrapLeft, TRUE);
  1309. COleObject *pobj = pme->GetObjectFromCp(cpObj);
  1310. if(pobj)
  1311. pobj->MeasureObj(1440, 1440, dulPicture, dvpJunk, dvpJunk, 0, pme->GetTflow());
  1312. }
  1313. plspap->uaLeft = pPF->_dxStartIndent + pPF->_dxOffset;
  1314. plspap->duaIndent = -pPF->_dxOffset;
  1315. LONG Alignment = pPF->_wNumberingStyle & 3;
  1316. if(pPF->_wNumbering && Alignment != tomAlignLeft)
  1317. {
  1318. // Move back by amount added to duaWidth in OlsGetAutoNumberInfo()
  1319. plspap->duaIndent -= (Alignment == tomAlignRight) ? pPF->_dxStartIndent
  1320. : pPF->_wNumberingTab ? pPF->_wNumberingTab
  1321. : pPF->_dxOffset;
  1322. }
  1323. if (dulPicture)
  1324. {
  1325. plspap->uaLeft = max(plspap->uaLeft, dulPicture);
  1326. // if hanging indent causes first line to overlap picture, shift it further
  1327. if (plspap->uaLeft + plspap->duaIndent < dulPicture)
  1328. plspap->duaIndent = dulPicture - plspap->uaLeft;
  1329. }
  1330. }
  1331. if(!pPF->InTable() && plspap->uaLeft < 0)
  1332. plspap->uaLeft = 0;
  1333. // Is this a bulleted paragraph? - ignore bullets in a password
  1334. if(pPF->_wNumbering && pme->fFirstInPara() && !pme->GetPasswordChar() &&
  1335. !pPF->IsNumberSuppressed())
  1336. {
  1337. CCcs *pccs = pme->GetCcsBullet(&pols->_CFBullet);
  1338. if (pccs)
  1339. pccs->Release();
  1340. plspap->grpf |= fFmiAnm;
  1341. WCHAR *pchAnm = pols->_szAnm;
  1342. pols->_cchAnm = 0;
  1343. if (pPF->IsRtl()) // Open character
  1344. *pchAnm++ = ' ';
  1345. //FUTURE (KeithCu) we turn off Indic digits if there is any Hebrew,
  1346. //which should be refined to do a better job with worldwide documents.
  1347. pols->_cchAnm += pPF->NumToStr(pchAnm, pme->GetNumber(),
  1348. (pme->GetPed()->GetCharFlags() & FHEBREW) ? 0 : fIndicDigits);
  1349. pchAnm += pols->_cchAnm;
  1350. if (pPF->IsRtl()) // End character for reverser
  1351. {
  1352. *pchAnm++ = wchObjectEnd;
  1353. pols->_cchAnm += 2; // Alloc space for open and close
  1354. }
  1355. *pchAnm++ = ' '; // Ensure a little extra space
  1356. *pchAnm++ = wchObjectEnd; // End character for Anm
  1357. pols->_cchAnm += 2;
  1358. }
  1359. return lserrNone;
  1360. }
  1361. /*
  1362. * OlsFetchTabs(pols, LSCP cp, plstabs, pfHangingTab, pduaHangingTab, pwchHangingTabLeader)
  1363. *
  1364. * @func
  1365. * Fetch tabs
  1366. *
  1367. * @rdesc
  1368. * LSERR
  1369. */
  1370. LSERR WINAPI OlsFetchTabs(
  1371. POLS pols, //(IN): (COls *)
  1372. LSCP cp, //(IN): Arbitrary cp value inside para
  1373. PLSTABS plstabs, //(OUT): Tabs array
  1374. BOOL * pfHangingTab, //(OUT): There is hanging tab
  1375. long * pduaHangingTab, //(OUT): dua of hanging tab
  1376. WCHAR * pwchHangingTabLeader) //(OUT): Leader of hanging tab
  1377. {
  1378. CMeasurer *pme = pols->GetMeasurer();
  1379. const CParaFormat *pPF = pme->Get_pPF();
  1380. const char rgchTabLeader[] = {0, '.', '-', '_', '_', '='};
  1381. LONG cTabCount = pPF->_bTabCount;
  1382. LONG i, iActual;
  1383. LSTBD * prgTab = pols->_rgTab;
  1384. const LONG *prgxTabs = pPF->GetTabs();
  1385. Assert(cTabCount <= MAX_TAB_STOPS && (prgxTabs || !cTabCount));
  1386. plstabs->duaIncrementalTab = pme->GetPed()->GetDefaultTab();
  1387. *pwchHangingTabLeader = 0;
  1388. *pduaHangingTab = pPF->_dxStartIndent + pPF->_dxOffset;
  1389. *pfHangingTab = pPF->_dxOffset > 0;
  1390. for(i = 0, iActual = 0; i < cTabCount; i++)
  1391. {
  1392. LONG tbAlign, tbLeader;
  1393. pPF->GetTab(i, &prgTab[iActual].ua, &tbAlign, &tbLeader, prgxTabs);
  1394. pme->SetUseTargetDevice(FALSE);
  1395. if (prgTab[iActual].ua > pme->_dulLayout)
  1396. break;
  1397. if(tbAlign <= tomAlignDecimal) // Don't include tomAlignBar
  1398. {
  1399. prgTab[iActual].lskt = (lsktab) tbAlign;
  1400. prgTab[iActual].wchTabLeader = rgchTabLeader[tbLeader];
  1401. iActual++;
  1402. }
  1403. }
  1404. plstabs->pTab = prgTab;
  1405. plstabs->iTabUserDefMac = iActual;
  1406. return lserrNone;
  1407. }
  1408. /*
  1409. * OlsCheckParaBoundaries (pols, cpOld, cpNew, pfChanged)
  1410. *
  1411. * @func
  1412. * Determine if para formatting between para containing cpOld and
  1413. * that containing cpNew are incompatible and shouldn't be formatted
  1414. * on the same line when connected by hidden text.
  1415. *
  1416. * @rdesc
  1417. * LSERR
  1418. */
  1419. LSERR WINAPI OlsCheckParaBoundaries(
  1420. POLS pols, //(IN): Interface object
  1421. LONG cpOld, //(IN): cp in one paragraph
  1422. LONG cpNew, //(IN): cp in another paragraph
  1423. BOOL * pfChanged) //(OUT): "Dangerous" change between para properties
  1424. {
  1425. // It's easier (and safer) to allow LS decide which para properties to take.
  1426. // Else we have to close objects (BiDi, for instance) before hidden EOP.
  1427. *pfChanged = fFalse; // they're always compatible
  1428. return lserrNone;
  1429. }
  1430. /*
  1431. * OlsGetRunCharWidths (pols, plrun, deviceID, lpwchRun, cwchRun, du,
  1432. * kTFlow, prgDu, pduRun, plimDu)
  1433. * @func
  1434. * Get run character widths
  1435. *
  1436. * @rdesc
  1437. * LSERR
  1438. */
  1439. LSERR WINAPI OlsGetRunCharWidths(
  1440. POLS pols, //(IN): Interface object
  1441. PLSRUN plsrun, //(IN): Run (cp here)
  1442. enum lsdevice deviceID, //(IN): Preview, reference, or absolute
  1443. LPCWSTR lpwchRun, //(IN): Run of characters
  1444. DWORD cwchRun, //(IN): Count of characters in run
  1445. long du, //(IN): Available space for characters
  1446. LSTFLOW kTFlow, //(IN): Text direction and orientation
  1447. int * prgDu, //(OUT): Widths of characters
  1448. long * pduRun, //(OUT): Sum of widths in rgDx[0] to rgDu[limDx-1]
  1449. long * plimDu) //(OUT): Number of widths fetched
  1450. {
  1451. CMeasurer *pme = pols->GetMeasurer();
  1452. BOOL fBullet = pols->SetRun(plsrun);
  1453. DWORD i = 0;
  1454. LONG dup, dupAdjust, duCalc = 0;
  1455. BOOL fGlyphRun = FALSE;
  1456. pme->SetUseTargetDevice(deviceID == lsdevReference);
  1457. CCcs *pccs = pme->Check_pccs(fBullet);
  1458. if(!pccs)
  1459. return lserrOutOfMemory;
  1460. #ifndef NOCOMPLEXSCRIPTS
  1461. if (pme->GetPed()->IsComplexScript() &&
  1462. plsrun->_a.eScript && !plsrun->IsBullet())
  1463. {
  1464. const SCRIPT_PROPERTIES *psp = pme->Getusp()->GeteProp(plsrun->_a.eScript);
  1465. if (psp->fComplex)
  1466. fGlyphRun = TRUE;
  1467. }
  1468. #endif
  1469. dupAdjust = pme->LUtoDU(plsrun->_pCF->_sSpacing);
  1470. for(;i < cwchRun; i++, lpwchRun++)
  1471. {
  1472. if (!fGlyphRun)
  1473. {
  1474. if (IsZerowidthCharacter(*lpwchRun))
  1475. dup = 0;
  1476. else
  1477. {
  1478. pccs->Include(*lpwchRun, dup);
  1479. dup = max(dup + dupAdjust, 1);
  1480. }
  1481. }
  1482. else
  1483. {
  1484. dup = 0;
  1485. if (!IsDiacriticOrKashida(*lpwchRun, 0))
  1486. dup = pccs->_xAveCharWidth;
  1487. }
  1488. duCalc += dup; // Keep running total of width
  1489. *prgDu++ = dup; // Store width in output array
  1490. if(dup + duCalc > du) // Width exceeds width available
  1491. {
  1492. i++; // Count this char as processed
  1493. break;
  1494. }
  1495. }
  1496. *plimDu = i; // Store total chars processed
  1497. *pduRun = duCalc; // Store output total width
  1498. return lserrNone;
  1499. }
  1500. /*
  1501. * OlsGetRunTextMetrics (pols, plsrun, deviceID, kTFlow, plsTxMet)
  1502. *
  1503. * @func
  1504. * Get run text metrics
  1505. *
  1506. * @rdesc
  1507. * LSERR
  1508. */
  1509. LSERR WINAPI OlsGetRunTextMetrics(
  1510. POLS pols, //(IN): interface object
  1511. PLSRUN plsrun, //(IN): run (cp here)
  1512. enum lsdevice deviceID, //(IN): presentation or reference
  1513. LSTFLOW kTFlow, //(IN): text direction and orientation
  1514. PLSTXM plsTxMet) //(OUT): Text metrics
  1515. {
  1516. CMeasurer *pme = pols->GetMeasurer();
  1517. BOOL fBullet = pols->SetRun(plsrun);
  1518. // Make sure right font is set for run
  1519. pme->SetUseTargetDevice(deviceID == lsdevReference);
  1520. CCcs *pccs = pme->Check_pccs(fBullet);
  1521. if(!pccs)
  1522. return lserrOutOfMemory;
  1523. LONG yFEAdjust = pccs->AdjustFEHeight(pme->FAdjustFELineHt());
  1524. LONG yDescent = pccs->_yDescent + yFEAdjust;
  1525. // Fill in metric structure
  1526. plsTxMet->dvAscent = pccs->_yHeight + (yFEAdjust << 1) - yDescent;
  1527. plsTxMet->dvDescent = yDescent;
  1528. plsTxMet->dvMultiLineHeight = plsTxMet->dvAscent + yDescent;
  1529. plsTxMet->fMonospaced = pccs->_fFixPitchFont;
  1530. if (plsrun->_pCF->_yOffset && pme->GetPF()->_bLineSpacingRule != tomLineSpaceExactly)
  1531. {
  1532. LONG yOffset, yAdjust;
  1533. pccs->GetOffset(plsrun->_pCF, deviceID == lsdevReference ? pme->_dvrInch :
  1534. pme->_dvpInch, &yOffset, &yAdjust);
  1535. if (yOffset < 0)
  1536. plsTxMet->dvDescent -= yOffset;
  1537. else
  1538. plsTxMet->dvAscent += yOffset;
  1539. }
  1540. return lserrNone;
  1541. }
  1542. /*
  1543. * OlsGetRunUnderlineInfo (pols, plsrun, pcheights, kTFlow, plsStInfo)
  1544. *
  1545. * @func
  1546. * Get run underline info
  1547. *
  1548. * @rdesc
  1549. * LSERR
  1550. */
  1551. LSERR WINAPI OlsGetRunUnderlineInfo(
  1552. POLS pols, //(IN): Interface object
  1553. PLSRUN plsrun, //(IN): Run (cp here)
  1554. PCHEIGHTS pcheights, //(IN): Height of line
  1555. LSTFLOW kTFlow, //(IN): Text direction and orientation
  1556. PLSULINFO plsUlInfo) //(OUT): Underline information
  1557. {
  1558. CMeasurer *pme = pols->GetMeasurer();
  1559. BOOL fBullet = pols->SetRun(plsrun);
  1560. const CDisplay *pdp = pme->GetPdp();
  1561. HDC hdc = pdp->GetDC();
  1562. // Initialize output buffer
  1563. ZeroMemory(plsUlInfo, sizeof(*plsUlInfo));
  1564. //REVIEW KeithCu
  1565. // Make sure right font is set for run
  1566. CCcs *pccs = pme->Check_pccs(fBullet);
  1567. if(!pccs)
  1568. return lserrOutOfMemory;
  1569. long dvpUlOffset = pccs->_dyULOffset;
  1570. plsUlInfo->cNumberOfLines = 1;
  1571. // Set underline type
  1572. if (plsrun->_pCF->_dwEffects & CFE_LINK)
  1573. plsUlInfo->kulbase = CFU_UNDERLINE;
  1574. else if (GetDeviceCaps(hdc, TECHNOLOGY) == DT_RASDISPLAY && GetTmpUnderline(plsrun->_pCF->_sTmpDisplayAttrIdx))
  1575. plsUlInfo->kulbase = GetTmpUnderline(plsrun->_pCF->_sTmpDisplayAttrIdx);
  1576. else if (plsrun->_pCF->_dwEffects & (CFE_UNDERLINE | CFE_REVISED))
  1577. plsUlInfo->kulbase = plsrun->_pCF->_bUnderlineType;
  1578. else
  1579. {
  1580. Assert(pme->GetPed()->GetCpAccelerator() == plsrun->_cp);
  1581. plsUlInfo->kulbase = CFU_UNDERLINE;
  1582. }
  1583. pdp->ReleaseDC(hdc);
  1584. LONG yDescent = pccs->_yDescent + pccs->AdjustFEHeight(pme->FAdjustFELineHt());
  1585. // Some fonts report invalid offset so we fix it up here
  1586. if(dvpUlOffset >= yDescent)
  1587. dvpUlOffset = yDescent - 1;
  1588. plsUlInfo->dvpFirstUnderlineOffset = dvpUlOffset;
  1589. plsUlInfo->dvpFirstUnderlineSize = pccs->_dyULWidth;
  1590. return lserrNone;
  1591. }
  1592. /*
  1593. * OlsGetRunStrikethroughInfo (pols, plsrun, pcheights, kTFlow, plsStInfo)
  1594. *
  1595. * @func
  1596. * Get run strikethrough info
  1597. *
  1598. * @rdesc
  1599. * LSERR
  1600. */
  1601. LSERR WINAPI OlsGetRunStrikethroughInfo(
  1602. POLS pols, //(IN): interface object
  1603. PLSRUN plsrun, //(IN): run
  1604. PCHEIGHTS pcheights, //(IN): height of line
  1605. LSTFLOW kTFlow, //(IN): text direction and orientation
  1606. PLSSTINFO plsStInfo) //(OUT): Strikethrough information
  1607. {
  1608. CMeasurer *pme = pols->GetMeasurer();
  1609. BOOL fBullet = pols->SetRun(plsrun);
  1610. AssertSz(plsrun->_pCF->_dwEffects & CFE_STRIKEOUT, "no strikeout");
  1611. // Make sure right font is set for run
  1612. CCcs *pccs = pme->Check_pccs(fBullet);
  1613. if(!pccs)
  1614. return lserrOutOfMemory;
  1615. // Default number of lines
  1616. plsStInfo->cNumberOfLines = 1;
  1617. plsStInfo->dvpLowerStrikethroughOffset = -pccs->_dySOOffset;
  1618. plsStInfo->dvpLowerStrikethroughSize = pccs->_dySOWidth;
  1619. return lserrNone;
  1620. }
  1621. /* OlsDrawUnderline (pols, plsrun, kUlbase, pptStart, dupUL, dvpUL,
  1622. * kTFlow, kDisp, prcClip)
  1623. * @func
  1624. * Draw underline
  1625. *
  1626. * @rdesc
  1627. * LSERR
  1628. */
  1629. LSERR WINAPI OlsDrawUnderline(
  1630. POLS pols, //(IN): interface object
  1631. PLSRUN plsrun, //(IN): run (cp) to use for underlining
  1632. UINT kUlbase, //(IN): underline kind
  1633. const POINT *pptStart, //(IN): starting position (top left)
  1634. DWORD dupUL, //(IN): underline width
  1635. DWORD dvpUL, //(IN): underline thickness
  1636. LSTFLOW lstflow, //(IN): text direction and orientation
  1637. UINT kDisp, //(IN): display mode - opaque, transparent
  1638. const RECT *prcClip) //(IN): clipping rectangle
  1639. {
  1640. CRenderer *pre = pols->GetRenderer();
  1641. Assert(pre->IsRenderer());
  1642. pols->SetRun(plsrun);
  1643. pre->Check_pccs();
  1644. pre->SetSelected(plsrun->IsSelected());
  1645. pre->SetFontAndColor(plsrun->_pCF);
  1646. if (pre->fDisplayDC() && GetTmpUnderline(plsrun->_pCF->_sTmpDisplayAttrIdx))
  1647. {
  1648. COLORREF crTmpUnderline;
  1649. GetTmpUnderlineColor(plsrun->_pCF->_sTmpDisplayAttrIdx, crTmpUnderline);
  1650. pre->SetupUnderline(kUlbase, 0, crTmpUnderline);
  1651. }
  1652. else
  1653. pre->SetupUnderline(kUlbase, plsrun->_pCF->_bUnderlineColor);
  1654. pre->RenderUnderline(lstflow == lstflowWS ? pptStart->x - dupUL - 1:
  1655. pptStart->x, pptStart->y, dupUL, dvpUL);
  1656. return lserrNone;
  1657. }
  1658. /*
  1659. * OlsDrawStrikethrough (pols, plsrun, kStbase, pptStart, dupSt, dvpSt,
  1660. * kTFlow, kDisp, prcClip)
  1661. * @func
  1662. * Draw strikethrough
  1663. *
  1664. * @rdesc
  1665. * LSERR
  1666. */
  1667. LSERR WINAPI OlsDrawStrikethrough(
  1668. POLS pols, //(IN): Interface object
  1669. PLSRUN plsrun, //(IN): run (cp) for strikethrough
  1670. UINT kStbase, //(IN): strikethrough kind
  1671. const POINT *pptStart, //(IN): starting position (top left)
  1672. DWORD dupSt, //(IN): strikethrough width
  1673. DWORD dvpSt, //(IN): strikethrough thickness
  1674. LSTFLOW lstflow, //(IN): text direction and orientation
  1675. UINT kDisp, //(IN): display mode - opaque, transparent
  1676. const RECT *prcClip) //(IN): clipping rectangle
  1677. {
  1678. CRenderer *pre = pols->GetRenderer();
  1679. Assert(pre->IsRenderer());
  1680. pols->SetRun(plsrun);
  1681. pre->SetSelected(plsrun->IsSelected());
  1682. pre->RenderStrikeOut(lstflow == lstflowWS ? pptStart->x - dupSt - 1:
  1683. pptStart->x, pptStart->y, dupSt, dvpSt);
  1684. return lserrNone;
  1685. }
  1686. /*
  1687. * OlsFInterruptUnderline(pols, plsrunFirst, cpLastFirst, plsrunSecond,
  1688. * cpStartSecond, pfInterruptUnderline)
  1689. * @func
  1690. * Says whether client wants to interrupt drawing of underline
  1691. * between the first and second runs
  1692. *
  1693. * @rdesc
  1694. * LSERR
  1695. */
  1696. LSERR WINAPI OlsFInterruptUnderline(
  1697. POLS pols, //(IN): Client context
  1698. PLSRUN plsrunFirst, //(IN): Run pointer for previous run
  1699. LSCP cpLastFirst, //(IN): cp of last character of previous run
  1700. PLSRUN plsrunSecond, //(IN): Run pointer for current run
  1701. LSCP cpStartSecond, //(IN): cp of first character of current run
  1702. BOOL * pfInterruptUnderline)//(OUT): Interrupt underline between runs?
  1703. {
  1704. CRenderer *pre = pols->GetRenderer();
  1705. Assert(pre->IsRenderer());
  1706. pre->SetSelected(FALSE); //Selection is handled below
  1707. COLORREF cr = pre->GetTextColor(plsrunFirst->_pCF);
  1708. // Interrupt underline if run text colors differ
  1709. *pfInterruptUnderline = cr != pre->GetTextColor(plsrunSecond->_pCF) ||
  1710. plsrunFirst->IsSelected() != plsrunSecond->IsSelected();
  1711. return lserrNone;
  1712. }
  1713. /*
  1714. * OlsDrawTextRun (pols, plsrun, fStrikeoutOkay, fUnderlineOkay, ppt, pwchRun,
  1715. * rgDupRun, cwchRun, lstflow, kDisp, pptRun, pheightsPres,
  1716. * dupRun, dupUlLimRun, prcClip)
  1717. * @func
  1718. * Draw text run
  1719. *
  1720. * @rdesc
  1721. * LSERR
  1722. */
  1723. LSERR WINAPI OlsDrawTextRun(
  1724. POLS pols, //(IN): Interface object
  1725. PLSRUN plsrun, //(IN): Run (cp) to use for text
  1726. BOOL fStrikeoutOkay, //(IN): TRUE <==> allow strikeout
  1727. BOOL fUnderlineOkay, //(IN): TRUE <==> allow underlining
  1728. const POINT *ppt, //(IN): Starting position
  1729. LPCWSTR pwchRun, //(IN): Run of characters
  1730. const int * rgDupRun, //(IN): Character widths
  1731. DWORD cwchRun, //(IN): Count of chars in run
  1732. LSTFLOW lstflow, //(IN): Text direction and orientation
  1733. UINT kDisp, //(IN): Display mode - opaque, transparent
  1734. const POINT *pptRun, //(IN): Starting point of run
  1735. PCHEIGHTS pheightsPres, //(IN): Presentation heights for run
  1736. long dupRun, //(IN): Presentation width for run
  1737. long dupUlLimRun, //(IN): Underlining limit
  1738. const RECT *prcClip) //(IN): Clipping rectangle
  1739. {
  1740. CRenderer *pre = pols->GetRenderer();
  1741. RECT rc = *prcClip;
  1742. Assert(pre->IsRenderer());
  1743. // Set up drawing point and options
  1744. BOOL fBullet = pols->SetRun(plsrun);
  1745. CCcs *pccs = pre->Check_pccs(fBullet);
  1746. if(!pccs)
  1747. return lserrOutOfMemory;
  1748. // v needs to be moved from baseline to top of character
  1749. POINTUV pt = {ppt->x, ppt->y - (pccs->_yHeight - pccs->_yDescent)};
  1750. if (lstflow == lstflowWS)
  1751. pt.u -= dupRun - 1;
  1752. pre->SetSelected(plsrun->IsSelected());
  1753. pre->SetFontAndColor(plsrun->_pCF);
  1754. if(!fBullet && pre->_fBackgroundColor)
  1755. {
  1756. if (pre->_fEraseOnFirstDraw)
  1757. pre->EraseLine();
  1758. kDisp = ETO_OPAQUE | ETO_CLIPPED;
  1759. SetBkMode(pre->_hdc, OPAQUE);
  1760. POINTUV ptCur = pre->GetCurPoint();
  1761. ptCur.u = pt.u;
  1762. pre->SetCurPoint(ptCur);
  1763. pre->SetClipLeftRight(dupRun);
  1764. RECTUV rcuv = pre->GetClipRect();
  1765. pre->GetPdp()->RectFromRectuv(rc, rcuv);
  1766. }
  1767. else if (!pre->_fEraseOnFirstDraw && cwchRun == 1 && pwchRun[0] == ' ')
  1768. return lserrNone; //Don't waste time drawing a space.
  1769. if (pre->_fEraseOnFirstDraw)
  1770. {
  1771. SetBkMode(pre->_hdc, OPAQUE);
  1772. pre->GetPdp()->RectFromRectuv(rc, pre->_rcErase);
  1773. kDisp |= ETO_OPAQUE;
  1774. }
  1775. pre->RenderExtTextOut(pt, kDisp, &rc, pwchRun, cwchRun, rgDupRun);
  1776. if (pre->_fEraseOnFirstDraw || !fBullet && pre->_fBackgroundColor)
  1777. {
  1778. SetBkMode(pre->_hdc, TRANSPARENT);
  1779. pre->_fEraseOnFirstDraw = FALSE;
  1780. }
  1781. return lserrNone;
  1782. }
  1783. /*
  1784. * GetBreakingClasses (pols, plsrun, cpLS, ch, pbrkclsBefore, pbrkclsAfter)
  1785. *
  1786. * @func
  1787. * Line services calls this callback for each run, to obtain the
  1788. * breaking classes (line breaking behaviors) for each character
  1789. *
  1790. * For Quill and RichEdit, the breaking class of a character is
  1791. * independent of whether it occurs Before or After a break opportunity.
  1792. *
  1793. * @rdesc
  1794. * LSERR
  1795. */
  1796. LSERR WINAPI OlsGetBreakingClasses(
  1797. POLS pols, //(IN): Interface object
  1798. PLSRUN plsrun, //(IN): Run (cp) to use for text
  1799. LSCP cpLs, //(IN): cp of the character
  1800. WCHAR ch, //(IN): Char to return breaking classes for
  1801. BRKCLS *pbrkclsBefore, //(OUT): Breaking class if ch is lead char in pair
  1802. BRKCLS *pbrkclsAfter) //(OUT): Breaking class if ch is trail char in pair
  1803. {
  1804. // Get line breaking class and report it twice
  1805. LCID lcid = 0;
  1806. CMeasurer * pme = pols->GetMeasurer();
  1807. if(W32->OnWin9x())
  1808. lcid = pme->GetCF()->_lcid;
  1809. #ifndef NOCOMPLEXSCRIPTS
  1810. long cpRe = pols->GetCpReFromCpLs(cpLs);
  1811. CTxtBreaker *pbrk = pme->GetPed()->_pbrk;
  1812. *pbrkclsBefore = *pbrkclsAfter = (pbrk && pbrk->CanBreakCp(BRK_WORD, cpRe)) ?
  1813. brkclsOpen :
  1814. GetKinsokuClass(ch, 0xFFFF, lcid);
  1815. #else
  1816. *pbrkclsBefore = *pbrkclsAfter = GetKinsokuClass(ch, 0xFFFF, lcid);
  1817. #endif
  1818. return lserrNone;
  1819. }
  1820. /*
  1821. * OlsFTruncateBefore (pols, plsrunCur, cpCur, wchCur, durCur, cpPrev, wchPrev,
  1822. * durPrev, durCut, pfTruncateBefore)
  1823. * @func
  1824. * Line services support function. This should always return
  1825. * FALSE for best performance
  1826. *
  1827. * @rdesc
  1828. * LSERR
  1829. */
  1830. LSERR WINAPI OlsFTruncateBefore(
  1831. POLS pols, // (IN): Client context
  1832. PLSRUN plsrunCur, // (IN): PLSRUN of cp
  1833. LSCP cpCur, // (IN): cp of truncation char
  1834. WCHAR wchCur, // (IN): Truncation character
  1835. long durCur, // (IN): Width of truncation char
  1836. PLSRUN plsrunPrev, // (IN): PLSRUN of cpPrev
  1837. LSCP cpPrev, // (IN): cp of truncation char
  1838. WCHAR wchPrev, // (IN): Truncation character
  1839. long durPrev, // (IN): Width of truncation character
  1840. long durCut, // (IN): Width from RM until end of current char
  1841. BOOL * pfTruncateBefore) // (OUT): Truncation point is before this char
  1842. {
  1843. *pfTruncateBefore = FALSE;
  1844. return lserrNone;
  1845. }
  1846. /*
  1847. * OlsCanBreakBeforeChar (pols, brkcls, pcond)
  1848. *
  1849. * @func
  1850. * Line services calls this callback for a break candidate following an
  1851. * inline object, to determine whether breaks are prevented, possible or
  1852. * mandatory
  1853. *
  1854. * @rdesc
  1855. * LSERR
  1856. */
  1857. LSERR WINAPI OlsCanBreakBeforeChar(
  1858. POLS pols, //(IN): Client context
  1859. BRKCLS brkcls, //(IN): Breaking class
  1860. BRKCOND *pcond) //(OUT): Corresponding break condition
  1861. {
  1862. switch (brkcls)
  1863. {
  1864. default:
  1865. *pcond = brkcondCan;
  1866. break;
  1867. case brkclsClose:
  1868. case brkclsNoStartIdeo:
  1869. case brkclsExclaInterr:
  1870. case brkclsGlueA:
  1871. *pcond = brkcondNever;
  1872. break;
  1873. case brkclsIdeographic:
  1874. case brkclsSpaceN:
  1875. case brkclsSlash:
  1876. *pcond = brkcondPlease;
  1877. break;
  1878. };
  1879. return lserrNone;
  1880. }
  1881. /*
  1882. * OlsCanBreakAfterChar (pols, brkcls, pcond)
  1883. *
  1884. * @func
  1885. * Line services calls this callback for a break candidate preceding an
  1886. * inline object, to determine whether breaks are prevented, possible or
  1887. * mandatory
  1888. *
  1889. * @rdesc
  1890. * LSERR
  1891. */
  1892. LSERR WINAPI OlsCanBreakAfterChar(
  1893. POLS pols, //(IN): Client context
  1894. BRKCLS brkcls, //(IN): Breaking class
  1895. BRKCOND *pcond) //(OUT): Corresponding break condition
  1896. {
  1897. switch (brkcls)
  1898. {
  1899. default:
  1900. *pcond = brkcondCan;
  1901. break;
  1902. case brkclsOpen:
  1903. case brkclsGlueA:
  1904. *pcond = brkcondNever;
  1905. break;
  1906. case brkclsIdeographic:
  1907. case brkclsSpaceN:
  1908. case brkclsSlash:
  1909. *pcond = brkcondPlease;
  1910. break;
  1911. };
  1912. return lserrNone;
  1913. }
  1914. #ifndef NOCOMPLEXSCRIPTS
  1915. // REVIEW FUTURE : JMO May want some version for non complex script ligatures.
  1916. /*
  1917. * OlsFInterruptShaping (pols, kTFlow, plsrunFirst, plsrunSecond, pfInterruptShaping)
  1918. *
  1919. * @func
  1920. * Line services calls this callback to find out if you
  1921. * would like to ligate across these two runs.
  1922. *
  1923. * @rdesc
  1924. * LSERR
  1925. */
  1926. LSERR WINAPI OlsFInterruptShaping(
  1927. POLS pols, //(IN): Client context
  1928. LSTFLOW kTFlow, //(IN): Text direction and orientation
  1929. PLSRUN plsrunFirst, //(IN): Run #1
  1930. PLSRUN plsrunSecond, //(IN): Run #2
  1931. BOOL *pfInterruptShaping) //(OUT): Shape across these 2 runs?
  1932. {
  1933. *pfInterruptShaping = FALSE;
  1934. const CCharFormat* pCFFirst = plsrunFirst->_pCF;
  1935. const CCharFormat* pCFSecond = plsrunSecond->_pCF;
  1936. Assert (plsrunFirst->_a.eScript && plsrunSecond->_a.eScript);
  1937. const DWORD dwMask = CFE_BOLD | CFE_ITALIC | CFM_SUBSCRIPT;
  1938. if (pCFFirst == pCFSecond ||
  1939. (plsrunFirst->_a.eScript == plsrunSecond->_a.eScript &&
  1940. !((pCFFirst->_dwEffects ^ pCFSecond->_dwEffects) & dwMask) &&
  1941. pCFFirst->_iFont == pCFSecond->_iFont &&
  1942. pCFFirst->_yOffset == pCFSecond->_yOffset &&
  1943. pCFFirst->_yHeight == pCFSecond->_yHeight))
  1944. {
  1945. // establish link
  1946. plsrunFirst->_pNext = plsrunSecond;
  1947. return lserrNone;
  1948. }
  1949. *pfInterruptShaping = TRUE;
  1950. return lserrNone;
  1951. }
  1952. // LS calls this callback to shape the codepoint string to a glyph indices string
  1953. // for handling glyph based script such as Arabic, Hebrew and Thai.
  1954. //
  1955. LSERR OlsGetGlyphs(
  1956. POLS pols,
  1957. PLSRUN plsrun,
  1958. LPCWSTR pwch,
  1959. DWORD cch,
  1960. LSTFLOW kTFlow,
  1961. PGMAP pgmap, // OUT: array of logical cluster information
  1962. PGINDEX* ppgi, // OUT: array of output glyph indices
  1963. PGPROP* ppgprop, // OUT: array of glyph's properties
  1964. DWORD* pcgi) // OUT: number of glyph generated
  1965. {
  1966. pols->SetRun(plsrun);
  1967. CMeasurer* pme = pols->GetMeasurer();
  1968. CUniscribe* pusp = pme->Getusp();
  1969. Assert (pusp);
  1970. WORD* pwgi;
  1971. SCRIPT_VISATTR *psva;
  1972. int cgi;
  1973. pme->SetGlyphing(TRUE);
  1974. // Glyphing doesn't care about the target device but always
  1975. // using target device reduces creation of Cccs in general.
  1976. pme->SetUseTargetDevice(TRUE);
  1977. AssertSz(IN_RANGE(1, plsrun->_a.eScript, SCRIPT_MAX_COUNT - 1), "Bad script ID!");
  1978. // Digit substitution
  1979. pusp->SubstituteDigitShaper(plsrun, pme);
  1980. if (!(cgi = (DWORD)pusp->ShapeString(plsrun, &plsrun->_a, pme, pwch, (int)cch, pwgi, pgmap, psva)))
  1981. {
  1982. const SCRIPT_ANALYSIS saUndef = {SCRIPT_UNDEFINED,0,0,0,0,0,0,{0}};
  1983. // Current font cant shape given string.
  1984. // Try SCRIPT_UNDEF so it generates invalid glyphs
  1985. if (!(cgi = (DWORD)pusp->ShapeString(plsrun, (SCRIPT_ANALYSIS*)&saUndef, pme, pwch, (int)cch, pwgi, pgmap, psva)))
  1986. {
  1987. // For whatever reason we still fails.
  1988. // Abandon glyph processing.
  1989. plsrun->_a.fNoGlyphIndex = TRUE;
  1990. cgi = (DWORD)pusp->ShapeString(plsrun, &plsrun->_a, pme, pwch, (int)cch, pwgi, pgmap, psva);
  1991. }
  1992. }
  1993. *pcgi = cgi;
  1994. DupShapeState(plsrun, cch);
  1995. *ppgi = (PGINDEX)pwgi;
  1996. *ppgprop = (PGPROP)psva;
  1997. pme->SetGlyphing(FALSE);
  1998. return lserrNone;
  1999. }
  2000. // LS calls this callback to find out glyph positioning for complex scripts
  2001. //
  2002. LSERR OlsGetGlyphPositions(
  2003. POLS pols,
  2004. PLSRUN plsrun,
  2005. LSDEVICE deviceID,
  2006. LPWSTR pwch,
  2007. PCGMAP pgmap,
  2008. DWORD cch,
  2009. PCGINDEX pgi,
  2010. PCGPROP pgprop,
  2011. DWORD cgi,
  2012. LSTFLOW kTFlow,
  2013. int* pgdx, // OUT: array of glyph advanced width
  2014. PGOFFSET pgduv) // OUT: array of offset between glyphs
  2015. {
  2016. pols->SetRun(plsrun);
  2017. CMeasurer* pme = pols->GetMeasurer();
  2018. CUniscribe* pusp = pme->Getusp();
  2019. Assert (pusp);
  2020. Assert(pgduv);
  2021. pme->SetGlyphing(TRUE);
  2022. // zero out before passing to shaping engine
  2023. ZeroMemory ((void*)pgduv, cgi*sizeof(GOFFSET));
  2024. pme->SetUseTargetDevice(deviceID == lsdevReference);
  2025. AssertSz(IN_RANGE(1, plsrun->_a.eScript, SCRIPT_MAX_COUNT - 1), "Bad script ID!");
  2026. if (!pusp->PlaceString(plsrun, &plsrun->_a, pme, pgi, cgi, (const SCRIPT_VISATTR*)pgprop, pgdx, pgduv, NULL))
  2027. {
  2028. SCRIPT_ANALYSIS saUndef = {SCRIPT_UNDEFINED,0,0,0,0,0,0,{0}};
  2029. if (!pusp->PlaceString(plsrun, &saUndef, pme, pgi, cgi, (const SCRIPT_VISATTR*)pgprop, pgdx, pgduv, NULL))
  2030. {
  2031. plsrun->_a.fNoGlyphIndex = TRUE;
  2032. pusp->PlaceString(plsrun, &plsrun->_a, pme, pgi, cgi, (const SCRIPT_VISATTR*)pgprop, pgdx, pgduv, NULL);
  2033. }
  2034. }
  2035. //Support spacing for base glyphs. Note this spreads apart clusters and breaks the lines which connect
  2036. //arabic text, but this might be okay.
  2037. if (plsrun->_pCF->_sSpacing)
  2038. {
  2039. LONG dupAdjust = pme->LUtoDU(plsrun->_pCF->_sSpacing);
  2040. for (DWORD gi = 0; gi < cgi; gi++)
  2041. if (pgdx[gi])
  2042. pgdx[gi] += dupAdjust;
  2043. }
  2044. DupShapeState(plsrun, cch);
  2045. pme->SetGlyphing(FALSE);
  2046. return lserrNone;
  2047. }
  2048. LSERR OlsDrawGlyphs(
  2049. POLS pols,
  2050. PLSRUN plsrun,
  2051. BOOL fStrikeOut,
  2052. BOOL fUnderline,
  2053. PCGINDEX pcgi,
  2054. const int* pgdx, // array of glyph width
  2055. const int* pgdxo, // array of original glyph width (before justification)
  2056. PGOFFSET pgduv, // array of glyph offset
  2057. PGPROP pgprop, // array of glyph's properties
  2058. PCEXPTYPE pgxtype, // array of expansion type
  2059. DWORD cgi,
  2060. LSTFLOW kTFlow,
  2061. UINT kDisp,
  2062. const POINT* pptRun,
  2063. PCHEIGHTS pHeight,
  2064. long dupRun,
  2065. long dupLimUnderline,
  2066. const RECT* prectClip)
  2067. {
  2068. BOOL fBullet = pols->SetRun(plsrun);
  2069. CRenderer* pre = pols->GetRenderer();
  2070. CUniscribe* pusp = pre->Getusp();
  2071. Assert(pusp && pre->IsRenderer());
  2072. pre->SetGlyphing(TRUE);
  2073. RECT rc = *prectClip;
  2074. CCcs* pccs = pre->Check_pccs(fBullet);
  2075. if (!pccs)
  2076. return lserrOutOfMemory;
  2077. // Apply fallback font if we need to
  2078. if (!fBullet)
  2079. pccs = pre->ApplyFontCache(plsrun->IsFallback(), plsrun->_a.eScript);
  2080. pre->SetSelected(plsrun->IsSelected());
  2081. pre->SetFontAndColor(plsrun->_pCF);
  2082. // v needs to be moved from baseline to top of character
  2083. POINTUV pt = {pptRun->x, pptRun->y - (pccs->_yHeight - pccs->_yDescent)};
  2084. if (kTFlow == lstflowWS)
  2085. pt.u -= dupRun - 1;
  2086. if(!fBullet && pre->_fBackgroundColor)
  2087. {
  2088. if (pre->_fEraseOnFirstDraw)
  2089. pre->EraseLine();
  2090. kDisp = ETO_OPAQUE | ETO_CLIPPED;
  2091. SetBkMode(pre->_hdc, OPAQUE);
  2092. POINTUV ptCur = pre->GetCurPoint();
  2093. ptCur.u = pt.u;
  2094. pre->SetCurPoint(ptCur);
  2095. pre->SetClipLeftRight(dupRun);
  2096. RECTUV rcuv = pre->GetClipRect();
  2097. pre->GetPdp()->RectFromRectuv(rc, rcuv);
  2098. }
  2099. if (rc.left >= rc.right || rc.top >= rc.bottom)
  2100. goto Exit;
  2101. if (pre->GetPdp()->IsMetafile() && !IsEnhancedMetafileDC(pre->GetDC()))
  2102. {
  2103. // -WMF metafile handling-
  2104. //
  2105. // If the rendering device is WMF metafile. We metafile the codepoint array
  2106. // instead of glyph indices. This requires that the target OS must know how to
  2107. // playback complex script text (shaping, Bidi algorithm, etc.).
  2108. // Metafiling glyph indices only works for EMF since the WMF's META_EXTTEXTOUT
  2109. // record stores the input string as an array of byte but a glyph index is 16-bit
  2110. // word element.
  2111. // WMF also must NOT be used to record ExtTextOutW call otherwise the Unicode
  2112. // string will be converted to mutlibyte text using system codepage. Anything
  2113. // outside the codepage then becomes '?'.
  2114. // We have the workaround for such case in REExtTextOut to make sure we only
  2115. // metafile ExtTextOutA to WMF. (wchao)
  2116. //
  2117. LONG cch;
  2118. const WCHAR* pwch = pre->GetPch(cch);
  2119. PINT piDx;
  2120. cch = min(cch, pre->GetCchLeftRunCF());
  2121. cch = min(cch, pre->GetLine()._cch - plsrun->_cp + pols->_cp);
  2122. // make sure that we record ETO with proper reading order.
  2123. kDisp |= plsrun->_a.fRTL ? ETO_RTLREADING : 0;
  2124. if (pusp->PlaceMetafileString(plsrun, pre, pwch, (int)cch, &piDx))
  2125. {
  2126. pre->RenderExtTextOut(pt, kDisp, &rc, pwch, cch, piDx);
  2127. goto Exit;
  2128. }
  2129. TRACEERRORSZ("Recording metafile failed!");
  2130. // Fall through... with unexpected error
  2131. // Else, metafile glyph indices for EMF...
  2132. }
  2133. if (pre->_fEraseOnFirstDraw)
  2134. {
  2135. SetBkMode(pre->_hdc, OPAQUE);
  2136. pre->GetPdp()->RectFromRectuv(rc, pre->_rcErase);
  2137. kDisp |= ETO_OPAQUE;
  2138. }
  2139. //This is duplicated from RenderExtTextOut but the params are different so simplest solution
  2140. //was to copy code.
  2141. if(pre->_fDisabled)
  2142. {
  2143. if(pre->_crForeDisabled != pre->_crShadowDisabled)
  2144. {
  2145. // The shadow should be offset by a hairline point, namely
  2146. // 3/4 of a point. Calculate how big this is in device units,
  2147. // but make sure it is at least 1 pixel.
  2148. DWORD offset = MulDiv(3, pre->_dvpInch, 4*72);
  2149. offset = max(offset, 1);
  2150. // Draw shadow
  2151. pre->SetTextColor(pre->_crShadowDisabled);
  2152. POINTUV ptT = pt;
  2153. ptT.u += offset;
  2154. ptT.v += offset;
  2155. POINT pt;
  2156. pre->GetPdp()->PointFromPointuv(pt, ptT, TRUE);
  2157. ScriptTextOut(pre->GetDC(), &pccs->_sc, pt.x, pt.y, kDisp, &rc, &plsrun->_a,
  2158. NULL, 0, pcgi, (int)cgi, pgdx, NULL, pgduv);
  2159. kDisp &= ~ETO_OPAQUE;
  2160. SetBkMode(pre->_hdc, TRANSPARENT);
  2161. }
  2162. pre->SetTextColor(pre->_crForeDisabled);
  2163. }
  2164. POINT ptStart;
  2165. pre->GetPdp()->PointFromPointuv(ptStart, pt, TRUE);
  2166. ScriptTextOut(pre->GetDC(), &pccs->_sc, ptStart.x, ptStart.y, kDisp, &rc, &plsrun->_a,
  2167. NULL, 0, pcgi, (int)cgi, pgdx, NULL, pgduv);
  2168. if (pre->_fEraseOnFirstDraw || !fBullet && pre->_fBackgroundColor)
  2169. {
  2170. SetBkMode(pre->_hdc, TRANSPARENT);
  2171. pre->_fEraseOnFirstDraw = FALSE;
  2172. }
  2173. Exit:
  2174. if (!fBullet)
  2175. pre->ApplyFontCache(0, 0); // reset font fallback if any
  2176. pre->SetGlyphing(FALSE);
  2177. return lserrNone;
  2178. }
  2179. #endif
  2180. /*
  2181. * OlsResetRunContents (pols, plsrun, cpFirstOld, dcpOld, cpFirstNew, dcpNew)
  2182. *
  2183. * @func
  2184. * Line Services calls this routine when a ligature
  2185. * or kern pair extends across run boundaries.
  2186. *
  2187. * We don't have to do anything special here if we are
  2188. * careful about how we use our PLSRUNs.
  2189. * @rdesc
  2190. * LSERR
  2191. */
  2192. LSERR WINAPI OlsResetRunContents(
  2193. POLS pols, //(IN): Client context
  2194. PLSRUN plsrun, //(IN): Run being combined
  2195. LSCP cpFirstOld, //(IN): cp of the first run being combined
  2196. LSDCP dcpOld, //(IN): dcp of the first run being combined
  2197. LSCP cpFirstNew, //(IN): new cp of the run
  2198. LSDCP dcpNew) //(IN): new dcp of the run
  2199. {
  2200. return lserrNone;
  2201. }
  2202. /*
  2203. * OlsCheckForDigit (pols, plsrun, wch, pfIsDigit)
  2204. *
  2205. * @func
  2206. * Get numeric separators needed, e.g., for decimal tabs
  2207. *
  2208. * @rdesc
  2209. * LSERR
  2210. */
  2211. LSERR WINAPI OlsCheckForDigit(
  2212. POLS pols, //(IN): pols
  2213. PLSRUN plsrun, //(IN): Run (cp here)
  2214. WCHAR wch, //(IN): Character to check
  2215. BOOL * pfIsDigit) //(OUT): This character is digit
  2216. {
  2217. WORD wType;
  2218. // We could get the run LCID to use for the first parm in the following
  2219. // call, but the digit property should be independent of LCID.
  2220. W32->GetStringTypeEx(0, CT_CTYPE1, &wch, 1, &wType);
  2221. *pfIsDigit = (wType & C1_DIGIT) != 0;
  2222. return lserrNone;
  2223. }
  2224. /*
  2225. * OlsGetBreakThroughTab(pols, uaRightMargin, uaTabPos, puaRightMarginNew)
  2226. *
  2227. * @func
  2228. * Just follow word95 behavior.
  2229. *
  2230. * @rdesc
  2231. * LSERR
  2232. */
  2233. LSERR WINAPI OlsGetBreakThroughTab(
  2234. POLS pols, //(IN): client context
  2235. long uaRightMargin, //(IN): right margin for breaking
  2236. long uaTabPos, //(IN): breakthrough tab position
  2237. long * puaRightMarginNew) //(OUT): new right margin
  2238. {
  2239. *puaRightMarginNew = 20 * 1440;
  2240. return lserrNone;
  2241. }
  2242. /*
  2243. * OlsFGetLastLineJustification(pols, lskj, lskal, endr, pfJustifyLastLine, plskalLine)
  2244. *
  2245. * @func
  2246. * Just say no to justify last line.
  2247. *
  2248. * @rdesc
  2249. * LSERR
  2250. */
  2251. LSERR WINAPI OlsFGetLastLineJustification(
  2252. POLS pols, //(IN): client context
  2253. LSKJUST lskj, //(IN): kind of justification
  2254. LSKALIGN lskal, //(IN): kind of alignment
  2255. ENDRES endr, //(IN): result of formatting
  2256. BOOL *pfJustifyLastLine, //(OUT): should last line be fully justified
  2257. LSKALIGN *plskalLine) //(OUT): kind of alignment for this line
  2258. {
  2259. *pfJustifyLastLine = FALSE;
  2260. *plskalLine = lskal;
  2261. return lserrNone;
  2262. }
  2263. /*
  2264. * OlsGetHyphenInfo(pols, plsrun, pkysr, pwchYsr)
  2265. *
  2266. * @func
  2267. * We don't support fancy YSR types, tell LS so.
  2268. *
  2269. * @rdesc
  2270. * LSERR
  2271. */
  2272. LSERR WINAPI OlsGetHyphenInfo(
  2273. POLS pols, //(IN): client context
  2274. PLSRUN plsrun, //(IN)
  2275. DWORD* pkysr, //(OUT): Ysr type - see "lskysr.h"
  2276. WCHAR* pwchYsr) //(OUT): Character code of YSR
  2277. {
  2278. *pkysr = kysrNil;
  2279. *pwchYsr = 0;
  2280. return lserrNone;
  2281. }
  2282. /*
  2283. * OlsHyphenate(pols, pclsHyphLast, cpBeginWord, cpExceed, plshyph)
  2284. *
  2285. * @func
  2286. * Prepare the buffer, and then call the client and ask them to hyphenate the word.
  2287. *
  2288. * Getting perfect word hyphenation is a complex topics which to do properly would
  2289. * require a lot of work. This code is simple and hopefully good enough. One difficulty
  2290. * for example is hidden text. The right thing to do here is to strip out hidden text and
  2291. * build up a cp mapping from the remaining text to it's cp in the backing store. Yuck.
  2292. *
  2293. * @rdesc
  2294. * LSERR
  2295. */
  2296. extern CHyphCache *g_phc;
  2297. LSERR WINAPI OlsHyphenate(
  2298. POLS pols, //(IN): client context
  2299. PCLSHYPH pclsHyphLast, //(IN): last seen hyphenation opportunity
  2300. LSCP cpBeginWord, //(IN): First CP of last word on line
  2301. LSCP cpExceed, //(IN): CP which exceeds column
  2302. PLSHYPH plsHyph) //(OUT): Hyphenation opportunity found
  2303. {
  2304. CMeasurer *pme = pols->GetMeasurer();
  2305. CTxtEdit *ped = pme->GetPed();
  2306. CHyphCache *phc = ped->GetHyphCache();
  2307. if (!phc)
  2308. return lserrOutOfMemory;
  2309. if (!pme->IsMeasure())
  2310. {
  2311. phc->GetAt(pme->GetLine()._ihyph, plsHyph->kysr, plsHyph->wchYsr);
  2312. plsHyph->cpYsr = pols->GetCpLsFromCpRe(pols->_cp + pme->GetCchLine()) - 1;
  2313. // No break in the range LS expect...
  2314. if (plsHyph->cpYsr < cpBeginWord || plsHyph->cpYsr >= cpExceed)
  2315. plsHyph->kysr = kysrNil;
  2316. return lserrNone;
  2317. }
  2318. cpBeginWord = pols->GetCpReFromCpLs(cpBeginWord);
  2319. cpExceed = pols->GetCpReFromCpLs(cpExceed);
  2320. //Strip off leading junk
  2321. pme->SetCp(cpBeginWord);
  2322. for (; cpBeginWord < cpExceed; cpBeginWord++, pme->Move(1))
  2323. {
  2324. WCHAR ch = pme->GetChar();
  2325. WORD type1;
  2326. W32->GetStringTypeEx(pme->GetCF()->_lcid, CT_CTYPE1, &ch, 1, &type1);
  2327. if (type1 & C1_ALPHA)
  2328. break;
  2329. }
  2330. LONG cpEndWord = cpBeginWord + pme->FindWordBreak(WB_RIGHTBREAK, ped->GetAdjustedTextLength());
  2331. //Strip off trailing junk
  2332. pme->SetCp(cpEndWord);
  2333. for (; cpEndWord > cpBeginWord; cpEndWord--, pme->Move(-1))
  2334. {
  2335. WCHAR ch = pme->GetPrevChar();
  2336. WORD type1;
  2337. W32->GetStringTypeEx(pme->GetCF()->_lcid, CT_CTYPE1, &ch, 1, &type1);
  2338. if (type1 & C1_ALPHA)
  2339. break;
  2340. }
  2341. int cchWord = cpEndWord - cpBeginWord;
  2342. //Don't hyphenate unless at least 5 chars in word and can have 2 chars before
  2343. if (cchWord >= 5 && cpExceed - cpBeginWord > 2)
  2344. {
  2345. CTempWcharBuf tb;
  2346. WCHAR *pszWord = tb.GetBuf(cchWord + 1);
  2347. pme->SetCp(cpBeginWord);
  2348. pme->_rpTX.GetText(cchWord, pszWord);
  2349. pszWord[cchWord] = 0;
  2350. cpExceed = min(cpExceed, cpBeginWord + cchWord - 1);
  2351. (*pme->GetPed()->_pfnHyphenate)(pszWord, pme->GetCF()->_lcid, cpExceed - cpBeginWord, (HYPHRESULT*)plsHyph);
  2352. plsHyph->cpYsr += cpBeginWord; //The client gives us an ich, we turn it into a CP
  2353. if (plsHyph->kysr != khyphNil && (plsHyph->cpYsr >= cpExceed || plsHyph->cpYsr < cpBeginWord) ||
  2354. !IN_RANGE(khyphNil, plsHyph->kysr, khyphDelAndChange))
  2355. {
  2356. AssertSz(FALSE, "Bad results from hyphenation proc: ichHyph or khyph are invalid.");
  2357. plsHyph->kysr = kysrNil;
  2358. }
  2359. else
  2360. plsHyph->cpYsr = pols->GetCpLsFromCpRe(plsHyph->cpYsr);
  2361. }
  2362. else
  2363. plsHyph->kysr = kysrNil;
  2364. //Cache into CLine
  2365. pme->GetLine()._ihyph = phc->Find(plsHyph->kysr, plsHyph->wchYsr);
  2366. return lserrNone;
  2367. }
  2368. /*
  2369. * OlsCheckRunKernability (pols, plsrunFirst, plsrunSecond, pfKernable)
  2370. *
  2371. * @func
  2372. * Return if you can kern across these two runs.
  2373. *
  2374. * @rdesc
  2375. * lserrNone
  2376. */
  2377. LSERR WINAPI OlsCheckRunKernability(
  2378. POLS pols,
  2379. PLSRUN plsrunFirst,
  2380. PLSRUN plsrunSecond,
  2381. BOOL * pfKernable)
  2382. {
  2383. *pfKernable = plsrunFirst->_pCF->CanKernWith(plsrunSecond->_pCF);
  2384. return lserrNone;
  2385. }
  2386. /*
  2387. * OlsGetRunCharKerning(pols, plsrun, deviceID, pchRun, cchRun, ktflow, rgdu)
  2388. *
  2389. * @func
  2390. * Fetch and return the kerning pairs to Line Services.
  2391. *
  2392. * @rdesc
  2393. * LSERR
  2394. */
  2395. LSERR WINAPI OlsGetRunCharKerning(
  2396. POLS pols,
  2397. PLSRUN plsrun,
  2398. LSDEVICE deviceID,
  2399. LPCWSTR pchRun,
  2400. DWORD cchRun,
  2401. LSTFLOW ktflow,
  2402. int * rgdu)
  2403. {
  2404. CMeasurer *pme = pols->GetMeasurer();
  2405. // Make sure right font is set for run
  2406. pme->SetUseTargetDevice(deviceID == lsdevReference);
  2407. pols->SetRun(plsrun);
  2408. CCcs *pccs = pme->Check_pccs();
  2409. const CCharFormat *pCF = plsrun->_pCF;
  2410. if(!pccs)
  2411. return lserrOutOfMemory;
  2412. CKernCache *pkc = fc().GetKernCache(pCF->_iFont, pCF->_wWeight, pCF->_dwEffects & CFE_ITALIC);
  2413. Assert(pkc); //SetLsChp ensures this exists AND kerning pairs exist.
  2414. for (DWORD ich = 0; ich < cchRun - 1; ich++)
  2415. rgdu[ich] = pkc->FetchDup(pchRun[ich], pchRun[ich + 1], pme->_pccs->_yHeightRequest);
  2416. return lserrNone;
  2417. }
  2418. /*
  2419. * OlsReleaseRun (pols, plsrun)
  2420. *
  2421. * @func
  2422. * We do nothing because the run is in an array and is
  2423. * released automatically.
  2424. *
  2425. * @rdesc
  2426. * LSERR
  2427. */
  2428. LSERR WINAPI OlsReleaseRun(
  2429. POLS pols, //(IN): interface object
  2430. PLSRUN plsrun) //(IN): run (cp) to use for underlining
  2431. {
  2432. return lserrNone;
  2433. }
  2434. /*
  2435. * OlsNewPtr(pols, cBytes)
  2436. *
  2437. * @func
  2438. * Memory allocator.
  2439. */
  2440. void* WINAPI OlsNewPtr(
  2441. POLS pols, //@parm Not used
  2442. DWORD cBytes) //@parm Count of bytes to alloc
  2443. {
  2444. return PvAlloc(cBytes, 0);
  2445. }
  2446. /*
  2447. * OlsDisposePtr(pols, pv)
  2448. *
  2449. * @func
  2450. * Memory deallocator.
  2451. */
  2452. void WINAPI OlsDisposePtr(
  2453. POLS pols, //@parm Not used
  2454. void * pv) //@parm [in]: ptr to free
  2455. {
  2456. FreePv(pv);
  2457. }
  2458. /*
  2459. * OlsDisposePtr(pols, pv, cBytes)
  2460. *
  2461. * @func
  2462. * Memory reallocator.
  2463. */
  2464. void* WINAPI OlsReallocPtr(
  2465. POLS pols, //@parm Not used
  2466. void * pv, //@parm [in/out]: ptr to realloc
  2467. DWORD cBytes) //@parm Count of bytes to realloc
  2468. {
  2469. return PvReAlloc(pv, cBytes);
  2470. }
  2471. const REVERSEINIT reverseinit =
  2472. {
  2473. REVERSE_VERSION,
  2474. wchObjectEnd
  2475. };
  2476. LSERR WINAPI OlsGetObjectHandlerInfo(
  2477. POLS pols,
  2478. DWORD idObj,
  2479. void * pObjectInfo)
  2480. {
  2481. switch (idObj)
  2482. {
  2483. case OBJID_REVERSE:
  2484. memcpy(pObjectInfo, (void *)&reverseinit, sizeof(REVERSEINIT));
  2485. break;
  2486. default:
  2487. AssertSz(0, "Undefined Object handler. Add missing case.");
  2488. }
  2489. return lserrNone;
  2490. }
  2491. #ifdef DEBUG
  2492. /* Debugging APIs */
  2493. void WINAPI OlsAssertFailed(
  2494. char *sz,
  2495. char *szFile,
  2496. int iLine)
  2497. {
  2498. AssertSzFn(sz, szFile, iLine);
  2499. }
  2500. #endif
  2501. extern const LSCBK lscbk =
  2502. {
  2503. OlsNewPtr, // pfnNewPtr
  2504. OlsDisposePtr, // pfnDisposePtr
  2505. OlsReallocPtr, // pfnReallocPtr
  2506. OlsFetchRun, // pfnFetchRun
  2507. OlsGetAutoNumberInfo, // pfnGetAutoNumberInfo
  2508. OlsGetNumericSeparators, // pfnGetNumericSeparators
  2509. OlsCheckForDigit, // pfnCheckForDigit
  2510. OlsFetchPap, // pfnFetchPap
  2511. OlsFetchTabs, // pfnFetchTabs
  2512. OlsGetBreakThroughTab, // pfnGetBreakThroughTab
  2513. OlsFGetLastLineJustification,// pfnFGetLastLineJustification
  2514. OlsCheckParaBoundaries, // pfnCheckParaBoundaries
  2515. OlsGetRunCharWidths, // pfnGetRunCharWidths
  2516. OlsCheckRunKernability, // pfnCheckRunKernability
  2517. OlsGetRunCharKerning, // pfnGetRunCharKerning
  2518. OlsGetRunTextMetrics, // pfnGetRunTextMetrics
  2519. OlsGetRunUnderlineInfo, // pfnGetRunUnderlineInfo
  2520. OlsGetRunStrikethroughInfo, // pfnGetRunStrikethroughInfo
  2521. 0, // pfnGetBorderInfo
  2522. OlsReleaseRun, // pfnReleaseRun
  2523. OlsHyphenate, // pfnHyphenate
  2524. OlsGetHyphenInfo, // pfnGetHyphenInfo
  2525. OlsDrawUnderline, // pfnDrawUnderline
  2526. OlsDrawStrikethrough, // pfnDrawStrikethrough
  2527. 0, // pfnDrawBorder
  2528. 0, // pfnDrawUnderlineAsText //REVIEW (keithcu) Need to implement this??
  2529. OlsFInterruptUnderline, // pfnFInterruptUnderline
  2530. 0, // pfnFInterruptShade
  2531. 0, // pfnFInterruptBorder
  2532. 0, // pfnShadeRectangle
  2533. OlsDrawTextRun, // pfnDrawTextRun
  2534. 0, // pfnDrawSplatLine
  2535. #ifdef NOCOMPLEXSCRIPTS
  2536. 0,
  2537. 0,
  2538. 0,
  2539. OlsResetRunContents, // pfnResetRunContents
  2540. 0,
  2541. #else
  2542. OlsFInterruptShaping, // pfnFInterruptShaping
  2543. OlsGetGlyphs, // pfnGetGlyphs
  2544. OlsGetGlyphPositions, // pfnGetGlyphPositions
  2545. OlsResetRunContents, // pfnResetRunContents
  2546. OlsDrawGlyphs, // pfnDrawGlyphs
  2547. #endif
  2548. 0, // pfnGetGlyphExpansionInfo
  2549. 0, // pfnGetGlyphExpansionInkInfo
  2550. 0, // pfnGetEms
  2551. 0, // pfnPunctStartLine
  2552. 0, // pfnModWidthOnRun
  2553. 0, // pfnModWidthSpace
  2554. 0, // pfnCompOnRun
  2555. 0, // pfnCompWidthSpace
  2556. 0, // pfnExpOnRun
  2557. 0, // pfnExpWidthSpace
  2558. 0, // pfnGetModWidthClasses
  2559. OlsGetBreakingClasses, // pfnGetBreakingClasses
  2560. OlsFTruncateBefore, // pfnFTruncateBefore
  2561. OlsCanBreakBeforeChar, // pfnCanBreakBeforeChar
  2562. OlsCanBreakAfterChar, // pfnCanBreakAfterChar
  2563. 0, // pfnFHangingPunct
  2564. 0, // pfnGetSnapGrid
  2565. 0, // pfnDrawEffects
  2566. 0, // pfnFCancelHangingPunct
  2567. 0, // pfnModifyCompAtLastChar
  2568. 0, // pfnEnumText
  2569. 0, // pfnEnumTab
  2570. 0, // pfnEnumPen
  2571. OlsGetObjectHandlerInfo, // pfnGetObjectHandlerInfo
  2572. #ifdef DEBUG
  2573. OlsAssertFailed // pfnAssertFailed
  2574. #else
  2575. 0 // pfnAssertFailed
  2576. #endif
  2577. };
  2578. #endif // NOLINESERVICES