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.

1579 lines
42 KiB

  1. /*
  2. * @doc
  3. *
  4. * @module - MEASURE.CPP |
  5. *
  6. * CMeasurer class
  7. *
  8. * Authors:
  9. * Original RichEdit code: David R. Fulmer <nl>
  10. * Christian Fortini, Murray Sargent, Rick Sailor
  11. *
  12. * Copyright (c) 1995-1998 Microsoft Corporation. All rights reserved.
  13. */
  14. #include "_common.h"
  15. #include "_measure.h"
  16. #include "_font.h"
  17. #include "_disp.h"
  18. #include "_edit.h"
  19. #include "_frunptr.h"
  20. #include "_objmgr.h"
  21. #include "_coleobj.h"
  22. ASSERTDATA
  23. // Note we set this maximum length as appropriate for Win95 since Win95 GDI can
  24. // only handle 16 bit values. We don't special case this so that both NT and
  25. // Win95 will behave the same way.
  26. // Note that the following obscure constant was empirically determined on Win95.
  27. const LONG lMaximumWidth = (3 * SHRT_MAX) / 4;
  28. void CMeasurer::Init(const CDisplay *pdp)
  29. {
  30. CTxtEdit * ped = GetPed();
  31. _pdp = pdp;
  32. _pddReference = pdp;
  33. _pccs = NULL;
  34. _pPF = NULL;
  35. _chPassword = ped->TxGetPasswordChar();
  36. _wNumber = 0;
  37. _fRenderer = FALSE;
  38. _fGlyphing = _fFallback = _fTarget = FALSE;
  39. _fAdjustFELineHt = !fUseUIFont() && pdp->IsMultiLine();
  40. if(pdp->GetWordWrap())
  41. {
  42. const CDevDesc *pddTarget = pdp->GetTargetDev();
  43. if(pddTarget)
  44. _pddReference = pddTarget;
  45. }
  46. _dypInch = pdp->GetDypInch();
  47. _dxpInch = pdp->GetDxpInch();
  48. _dtPres = GetDeviceCaps(_pdp->_hdc, TECHNOLOGY);
  49. if (pdp->IsMain())
  50. {
  51. _dypInch = MulDiv(_dypInch, pdp->GetZoomNumerator(), pdp->GetZoomDenominator());
  52. _dxpInch = MulDiv(_dxpInch, pdp->GetZoomNumerator(), pdp->GetZoomDenominator());
  53. }
  54. if (pdp->SameDevice(_pddReference))
  55. {
  56. _dyrInch = _dypInch;
  57. _dxrInch = _dxpInch;
  58. _dtRef = _dtPres;
  59. }
  60. else
  61. {
  62. _dyrInch = _pddReference->GetDypInch();
  63. _dxrInch = _pddReference->GetDxpInch();
  64. _dtRef = GetDeviceCaps(_pddReference->_hdc, TECHNOLOGY);
  65. }
  66. }
  67. CMeasurer::CMeasurer (const CDisplay* const pdp) : CRchTxtPtr (pdp->GetPed())
  68. {
  69. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CMeasurer::CMeasurer");
  70. Init(pdp);
  71. }
  72. CMeasurer::CMeasurer (const CDisplay* const pdp, const CRchTxtPtr &tp) : CRchTxtPtr (tp)
  73. {
  74. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CMeasurer::CMeasurer");
  75. Init(pdp);
  76. }
  77. CMeasurer::~CMeasurer()
  78. {
  79. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CMeasurer::~CMeasurer");
  80. if(_pccs)
  81. _pccs->Release();
  82. }
  83. /*
  84. * CMeasurer::SetGlyphing(fGlyphing)
  85. *
  86. * @mfunc
  87. * A state flag inside the measurer to record whether or not you
  88. * are in the process of doing glyphing. If we are in a situation
  89. * where the _pddReference is a printer device, then we need to
  90. * throw away the _pccs.
  91. */
  92. void CMeasurer::SetGlyphing(
  93. BOOL fGlyphing) //@parm Currently doing glyphing
  94. {
  95. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CMeasurer::SetGlyphing");
  96. Assert(fGlyphing == TRUE || fGlyphing == FALSE);
  97. if (fGlyphing != _fGlyphing)
  98. {
  99. if (_dtRef == DT_RASPRINTER)
  100. {
  101. if (_pccs)
  102. _pccs->Release();
  103. _pccs = NULL;
  104. }
  105. _fGlyphing = fGlyphing;
  106. }
  107. }
  108. /*
  109. * CMeasurer::SetUseTargetDevice(fUseTargetDevice)
  110. *
  111. * @mfunc
  112. * Sets whether you want to use the target device or not
  113. * for getting metrics
  114. * FUTURE (keithcu) Make this a parameter
  115. */
  116. void CMeasurer::SetUseTargetDevice(
  117. BOOL fUseTargetDevice) //@parm Use target device metrics?
  118. {
  119. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CMeasurer::SetUseTargetDevice");
  120. Assert(fUseTargetDevice == TRUE || fUseTargetDevice == FALSE);
  121. if (fUseTargetDevice != _fTarget)
  122. {
  123. if (_dypInch != _dyrInch && _dxpInch != _dxrInch)
  124. {
  125. if (_pccs)
  126. _pccs->Release();
  127. _pccs = NULL;
  128. }
  129. _fTarget = fUseTargetDevice;
  130. }
  131. }
  132. /*
  133. * CMeasurer::NewLine (fFirstInPara)
  134. *
  135. * @mfunc
  136. * Initialize this measurer at the start of a new line
  137. */
  138. void CMeasurer::NewLine(
  139. BOOL fFirstInPara) //@parm Flag for setting up _bFlags
  140. {
  141. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CMeasurer::NewLine");
  142. _li.Init(); // Zero all members
  143. if(fFirstInPara)
  144. _li._bFlags = fliFirstInPara; // Need to know if first in para
  145. }
  146. /*
  147. * CMeasurer::NewLine(&li)
  148. *
  149. * @mfunc
  150. * Initialize this measurer at the start of a given line
  151. */
  152. void CMeasurer::NewLine(
  153. const CLine &li)
  154. {
  155. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CMeasurer::NewLine");
  156. _li = li;
  157. _li._cch = 0;
  158. _li._cchWhite = 0;
  159. _li._xWidth = 0;
  160. // Can't calculate xLeft till we get an HDC
  161. _li._xLeft = 0;
  162. _wNumber = _li._bNumber;
  163. }
  164. /*
  165. * CMeasurer::MaxWidth()
  166. *
  167. * @mfunc
  168. * Get maximum width for line
  169. *
  170. * @rdesc
  171. * Maximum width for a line
  172. */
  173. LONG CMeasurer::MaxWidth()
  174. {
  175. LONG xWidth = lMaximumWidth;
  176. if(_pdp->GetWordWrap())
  177. {
  178. // Only main display has a caret
  179. LONG xCaret = (_pdp->IsMain() && !GetPed()->TxGetReadOnly())
  180. ? dxCaret : 0;
  181. // Calculate display width
  182. LONG xDispWidth = _pdp->GetMaxPixelWidth();
  183. if(!_pdp->SameDevice(_pddReference) && _fTarget)
  184. {
  185. // xWidthMax is calculated to the size of the screen DC. If
  186. // there is a target device with different characteristics
  187. // we need to convert the width to the target device's width
  188. xDispWidth = _pddReference->ConvertXToDev(xDispWidth, _pdp);
  189. }
  190. xWidth = xDispWidth - MeasureRightIndent() - _li._xLeft - xCaret;
  191. }
  192. return (xWidth > 0) ? xWidth : 0;
  193. }
  194. /*
  195. * CMeasurer::MeasureText (cch)
  196. *
  197. * @mfunc
  198. * Measure a stretch of text from current running position.
  199. *
  200. * @rdesc
  201. * width of text (in device units), < 0 if failed
  202. */
  203. LONG CMeasurer::MeasureText(
  204. LONG cch) //@parm Number of characters to measure
  205. {
  206. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CMeasurer::MeasureText");
  207. if(Measure(0x7fffffff, cch, 0) == MRET_FAILED)
  208. return -1;
  209. return min(_li._xWidth, MaxWidth());
  210. }
  211. /*
  212. * CMeasurer::MeasureLine (cchMax, xWidthMax, uiFlags, pliTarget)
  213. *
  214. * @mfunc
  215. * Measure a line of text from current cp and determine line break.
  216. * On return *this contains line metrics for _pddReference device.
  217. *
  218. * @rdesc
  219. * TRUE if success, FALSE if failed
  220. */
  221. BOOL CMeasurer::MeasureLine(
  222. LONG cchMax, //@parm Max chars to process (-1 if no limit)
  223. LONG xWidthMax, //@parm max width to process (-1 uses CDisplay width)
  224. UINT uiFlags, //@parm Flags controlling the process (see Measure())
  225. CLine *pliTarget) //@parm Returns target-device line metrics (optional)
  226. {
  227. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CMeasurer::MeasureLine");
  228. // This state must be preserved across the two possible line width
  229. // calculations so we save it here.
  230. BYTE bNumberSave = _li._bNumber;
  231. const CDevDesc *pddTarget = NULL;
  232. if(_pdp->GetWordWrap())
  233. {
  234. // Target devices are only interesting if word wrap is on because the
  235. // only really interesting thing a target device can tell us is where
  236. // the word breaks will occur.
  237. pddTarget = _pdp->GetTargetDev();
  238. if(pddTarget)
  239. SetUseTargetDevice(TRUE);
  240. }
  241. // Compute line break
  242. LONG lRet = Measure(xWidthMax, cchMax, uiFlags);
  243. // Stop here if failed
  244. if(lRet == MRET_FAILED)
  245. return FALSE;
  246. // Return target metrics if requested
  247. if(pliTarget)
  248. *pliTarget = _li;
  249. if(pddTarget)
  250. {
  251. // We just use this flag as an easy way to get the recomputation to occur.
  252. lRet = MRET_NOWIDTH;
  253. }
  254. SetUseTargetDevice(FALSE);
  255. // Recompute metrics on rendering device
  256. if(lRet == MRET_NOWIDTH)
  257. {
  258. long cch = _li._cch;
  259. Advance(-cch); // move back to BOL
  260. NewLine(uiFlags & MEASURE_FIRSTINPARA);
  261. // Restore the line number
  262. _li._bNumber = bNumberSave;
  263. lRet = Measure(0x7fffffff, cch, uiFlags);
  264. if(lRet)
  265. {
  266. Assert(lRet != MRET_NOWIDTH);
  267. return FALSE;
  268. }
  269. }
  270. // Now that we know the line width, compute line shift due
  271. // to alignment, and add it to the left position
  272. _li._xLeft += MeasureLineShift();
  273. return TRUE;
  274. }
  275. /*
  276. * CMeasurer::RecalcLineHeight ()
  277. *
  278. * @mfunc
  279. * Reset height of line we are measuring if new run of text is taller
  280. * than current maximum in line.
  281. */
  282. void CMeasurer::RecalcLineHeight(
  283. CCcs *pccs, const CCharFormat * const pCF)
  284. {
  285. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CMeasurer::RecalcLineHeight");
  286. // Compute line height
  287. LONG yOffset, yAdjust;
  288. pccs->GetOffset(pCF, _fTarget ? _dyrInch : _dypInch, &yOffset, &yAdjust);
  289. LONG yHeight = pccs->_yHeight;
  290. LONG yDescent = pccs->_yDescent;
  291. SHORT yFEAdjust = pccs->AdjustFEHeight(fAdjustFELineHt());
  292. if (yFEAdjust)
  293. {
  294. yHeight += (yFEAdjust << 1);
  295. yDescent += yFEAdjust;
  296. }
  297. LONG yAscent = yHeight - yDescent;
  298. LONG yAboveBase = max(yAscent, yAscent + yOffset);
  299. LONG yBelowBase = max(yDescent, yDescent - yOffset);
  300. _li._yHeight = (SHORT)(max(yAboveBase, _li._yHeight - _li._yDescent) +
  301. max(yBelowBase, _li._yDescent));
  302. _li._yDescent = (SHORT)max(yBelowBase, _li._yDescent);
  303. }
  304. /*
  305. * CMeasurer::Measure (xWidthMax, cchMax, uiFlags)
  306. *
  307. * @mfunc
  308. * Measure given amount of text, start at current running position
  309. * and storing # chars measured in _cch.
  310. * Can optionally determine line break based on a xWidthMax and
  311. * break out at that point.
  312. *
  313. * @rdesc
  314. * 0 success
  315. * MRET_FAILED if failed
  316. * MRET_NOWIDTH if second pass is needed to compute correct width
  317. *
  318. * @devnote
  319. * The uiFlags parameter has the following meanings:
  320. * MEASURE_FIRSTINPARA this is first line of paragraph
  321. * MEASURE_BREAKATWORD break out on a word break
  322. * MEASURE_BREAKATWIDTH break closest possible to xWidthMax
  323. *
  324. * The calling chain must be protected by a CLock, since this present
  325. * routine access the global (shared) FontCache facility.
  326. */
  327. LONG CMeasurer::Measure(
  328. LONG xWidthMax, //@parm Max width of line (-1 uses CDisplay width)
  329. LONG cchMax, //@parm Max chars to process (-1 if no limit)
  330. UINT uiFlags) //@parm Flags controlling the process (see above)
  331. {
  332. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CMeasurer::Measure");
  333. LONG cch; // cchChunk count down
  334. LONG cchChunk; // cch of cst-format contiguous run
  335. LONG cchNonWhite; // cch of last nonwhite char in line
  336. LONG cchText = GetTextLength();
  337. unsigned ch; // Temporary char
  338. BOOL fFirstInPara = uiFlags & MEASURE_FIRSTINPARA;
  339. BOOL fLastChObj = FALSE;
  340. LONG lRet = 0;
  341. const WCHAR*pch;
  342. CTxtEdit * ped = GetPed();
  343. COleObject *pobj;
  344. LONG xCaret = dxCaret;
  345. LONG xAdd = 0; // Character width
  346. LONG xSoftHyphen = 0; // Most recent soft hyphen width
  347. LONG xWidthNonWhite; // xWidth for last nonwhite char in line
  348. LONG xWidthMaxOverhang; // Max xWidth with current run's overhang
  349. // taken into consideration.
  350. // This variable is used to keep track of whether there is a height change
  351. // so that we know whether we need to recalc the line in certain line break cases.
  352. BOOL fHeightChange = FALSE;
  353. const INT MAX_SAVED_WIDTHS = 31; // power of 2 - 1
  354. INT i, index, iSavedWidths = 0;
  355. struct {
  356. SHORT width;
  357. SHORT xLineOverhang;
  358. SHORT yHeight;
  359. SHORT yDescent;
  360. } savedWidths[MAX_SAVED_WIDTHS+1];
  361. _pPF = GetPF(); // Be sure current CParaFormat
  362. // ptr is up to date
  363. BOOL fInTable = _pPF->InTable();
  364. // If line spacing or space before/after, measure from beginning of line
  365. if (_li._cch && (_pPF->_bLineSpacingRule || _pPF->_dySpaceBefore ||
  366. _pPF->_dySpaceAfter || fInTable))
  367. {
  368. Advance(-_li._cch);
  369. NewLine(fFirstInPara);
  370. }
  371. // Init fliFirstInPara flag for new line
  372. if(fFirstInPara)
  373. {
  374. _li._bFlags |= fliFirstInPara;
  375. if(IsInOutlineView() && IsHeadingStyle(_pPF->_sStyle))
  376. _li._yHeight = (short)max(_li._yHeight, BITMAP_HEIGHT_HEADING + 1);
  377. }
  378. AssertSz(!_pPF->IsListNumbered() && !_wNumber ||
  379. (uiFlags & MEASURE_BREAKBEFOREWIDTH) || !_pdp->IsMultiLine() ||
  380. _wNumber > 20 || _wNumber == (i = GetParaNumber()),
  381. "CMeasurer::Measure: incorrect list number");
  382. _li._xLeft = MeasureLeftIndent(); // Set left indent
  383. // Compute width to break out at
  384. if(xWidthMax < 0)
  385. {
  386. xWidthMax = MaxWidth(); // MaxWidth includes caret size
  387. xCaret = 0;
  388. }
  389. else
  390. {
  391. // (AndreiB) xWidthMax that's coming down to us is always calculated
  392. // with respect to the screen DC. The only scenario it comes into play
  393. // however is in TxGetNaturalSize, which may output a slightly
  394. // different result because of that.
  395. if(!_pdp->SameDevice(_pddReference) && _fTarget)
  396. {
  397. // xWidthMax is calculated to the size of the screen DC. If
  398. // there is a target device with different characteristics
  399. // we need to convert the width to the target device's width
  400. xWidthMax = _pddReference->ConvertXToDev(xWidthMax, _pdp);
  401. }
  402. }
  403. // For overhang support, we test against this adjusted widthMax.
  404. xWidthMaxOverhang = xWidthMax;
  405. // Are we ignoring the offset of the characters for the measure?
  406. if(!(uiFlags & MEASURE_IGNOREOFFSET))
  407. {
  408. // No - then take it from the max
  409. xWidthMaxOverhang -= (_li._xLineOverhang + xCaret);
  410. }
  411. // Compute max count of characters to process
  412. cch = cchText - GetCp();
  413. if(cchMax < 0 || cchMax > cch)
  414. cchMax = cch;
  415. cchNonWhite = _li._cch; // Default nonwhite parms
  416. xWidthNonWhite = _li._xWidth;
  417. for( ; cchMax > 0; // Measure up to cchMax
  418. cchMax -= cchChunk, Advance(cchChunk)) // chars
  419. {
  420. pch = GetPch(cch);
  421. cch = min(cch, cchMax); // Compute constant-format
  422. cchChunk = GetCchLeftRunCF();
  423. cch = min(cch, cchChunk); // Counter for next while
  424. cchChunk = cch; // Save chunk size
  425. const CCharFormat *pCF = GetCF();
  426. DWORD dwEffects = pCF->_dwEffects;
  427. if(dwEffects & CFE_HIDDEN) // Ignore hidden text
  428. {
  429. _li._cch += cchChunk;
  430. continue;
  431. }
  432. if(!Check_pccs()) // Be sure _pccs is current
  433. return MRET_FAILED;
  434. xWidthMaxOverhang = xWidthMax; // Overhang reduces max.
  435. // Are we ignoring offset of characters for the measure?
  436. if(!(uiFlags & MEASURE_IGNOREOFFSET))
  437. {
  438. // No - then take it from the max
  439. xWidthMaxOverhang -= (_pccs->_xOverhang + xCaret);
  440. }
  441. // Adjust line height for new format run
  442. if(cch > 0 && *pch && (IsRich() || ped->HasObjects()))
  443. {
  444. // Note: the EOP only contributes to the height calculation for the
  445. // line if there are no non-white space characters on the line or
  446. // the paragraph is a bullet paragraph. The bullet paragraph
  447. // contribution to the line height is done in AdjustLineHeight.
  448. // REVIEW (Victork)
  449. // Another, similar topic is height of spaces.
  450. // They doesn't (normally) influence line height in LS,
  451. // they do in CMeasurer::Measure code.
  452. // Proposed ways to solve it:
  453. // - have fSpacesOnly flag in run
  454. // - move current (line height) logic down after next character-scanning loop
  455. if(!cchNonWhite || *pch != CR && *pch != LF)
  456. {
  457. // Determine if the current run is the tallest text on this
  458. // line and if so, increase the height of the line.
  459. LONG yHeightOld = _li._yHeight;
  460. RecalcLineHeight(_pccs, pCF);
  461. // Test for a change in line height. This only happens when
  462. // this is not the first character in the line and (surprise)
  463. // the height changes.
  464. if (yHeightOld && yHeightOld != _li._yHeight)
  465. fHeightChange = TRUE;
  466. }
  467. }
  468. while(cch > 0)
  469. { // Process next char
  470. xAdd = 0; // Default zero width
  471. ch = *pch;
  472. if(_chPassword && !IN_RANGE(LF, ch, CR))
  473. ch = _chPassword;
  474. #ifdef UNICODE_SURROGATES
  475. if (IN_RANGE(0xD800, ch, 0xDFFF) && cch > 1 &&
  476. IN_RANGE(0xDC00, *(pch+1), 0xDFFF)) // Unicode extended char
  477. {
  478. // Convert to multiplane char (nibble 4 may be nonzero).
  479. // _pccs->Include(ch, xAdd) will project it down into plane 0
  480. // since it truncates to 16-bits. The currently selected font
  481. // should be correct for the plane given by (nibble 4) + 1.
  482. ch = WCHAR((ch << 10) | (*pch & 0x3FF));
  483. _li._bFlags |= fliHasSurrogates; // Warn renderer
  484. }
  485. else // AllCaps not supported
  486. #endif // for surrogates
  487. if(dwEffects & CFE_ALLCAPS)
  488. ch = (WCHAR)CharUpper((WCHAR *)(DWORD_PTR)ch); // See SDK to understand
  489. // weird casts here
  490. if(ch == WCH_EMBEDDING)
  491. {
  492. _li._bFlags |= fliHasOle;
  493. pobj = ped->GetObjectMgr()->GetObjectFromCp
  494. (GetCp() + cchChunk - cch);
  495. if(pobj)
  496. {
  497. LONG yAscent, yDescent;
  498. pobj->MeasureObj(_fTarget ? _dyrInch : _dypInch,
  499. _fTarget ? _dxrInch : _dxpInch,
  500. xAdd, yAscent, yDescent, _li._yDescent);
  501. // Only update height for line if the object is going
  502. // to be on this line.
  503. if(!_li._cch || _li._xWidth + xAdd <= xWidthMaxOverhang)
  504. {
  505. if (yAscent > _li._yHeight - _li._yDescent)
  506. _li._yHeight = yAscent + _li._yDescent;
  507. }
  508. }
  509. if(_li._xWidth + xAdd > xWidthMaxOverhang)
  510. fLastChObj = TRUE;
  511. }
  512. // The following if succeeds if ch isn't a CELL, BS, TAB, LF,
  513. // VT, FF, or CR
  514. else if(!IN_RANGE(CELL, ch, CR)) // Not TAB or EOP
  515. {
  516. // Get char width if not Unicode low surrogate
  517. if (
  518. #ifdef UNICODE_SURROGATES
  519. !IN_RANGE(0xDC00, ch, 0xDFFF) &&
  520. #endif
  521. !IN_RANGE(0x300, ch, 0x36F) &&
  522. !_pccs->Include(ch, xAdd))
  523. {
  524. AssertSz(FALSE, "CMeasurer::Measure char not in font");
  525. return MRET_FAILED;
  526. }
  527. if(ch == SOFTHYPHEN)
  528. {
  529. _li._bFlags |= fliHasTabs; // Setup RenderChunk()
  530. // get the width of hyphen instead
  531. if (!_pccs->Include('-', xAdd))
  532. {
  533. AssertSz(FALSE, "CMeasurer::Measure char not in font");
  534. return MRET_FAILED;
  535. }
  536. if(_li._xWidth + xAdd < xWidthMaxOverhang || !_li._cch)
  537. {
  538. xSoftHyphen = xAdd; // Save soft hyphen width
  539. xAdd = 0; // Use 0 unless at EOL
  540. }
  541. }
  542. else if (ch == EURO)
  543. _li._bFlags |= fliHasSpecialChars;
  544. }
  545. else if(ch == TAB || ch == CELL)
  546. {
  547. _li._bFlags |= fliHasTabs;
  548. xAdd = MeasureTab(ch);
  549. }
  550. else if(ch == FF && ped->Get10Mode()) // RichEdit 1.0 treats
  551. _pccs->Include(ch, xAdd); // FFs as normal chars
  552. else // Done with line
  553. goto eop; // Go process EOP chars
  554. index = iSavedWidths++ & MAX_SAVED_WIDTHS;
  555. savedWidths[index].width = (SHORT)xAdd;
  556. savedWidths[index].xLineOverhang = _li._xLineOverhang;
  557. savedWidths[index].yHeight = _li._yHeight;
  558. savedWidths[index].yDescent = _li._yDescent;
  559. _li._xWidth += xAdd;
  560. if(_li._xWidth > xWidthMaxOverhang &&
  561. (uiFlags & MEASURE_BREAKBEFOREWIDTH || _li._cch > 0))
  562. goto overflow;
  563. _li._cch++;
  564. pch++;
  565. cch--;
  566. if(ch != TEXT(' ') /*&& ch != TAB*/) // If not whitespace char,
  567. {
  568. cchNonWhite = _li._cch; // update nonwhitespace
  569. xWidthNonWhite = _li._xWidth; // count and width
  570. }
  571. } // while(cch > 0)
  572. } // for(;cchMax > 0;...)
  573. goto eol; // All text exhausted
  574. // End Of Paragraph char encountered (CR, LF, VT, or FF, but mostly CR)
  575. eop:
  576. Advance(cchChunk - cch); // Position tp at EOP
  577. cch = AdvanceCRLF(); // Bypass possibly multibyte EOP
  578. _li._cchEOP = (BYTE)cch; // Store EOP cch
  579. _li._cch += cch; // Increment line count
  580. if(ch == CR || ped->fUseCRLF() && ch == LF)
  581. _li._bFlags |= fliHasEOP;
  582. AssertSz(ped->fUseCRLF() || cch == 1,
  583. "CMeasurer::Measure: EOP isn't a single char");
  584. AssertSz(_pdp->IsMultiLine() || GetCp() == cchText,
  585. "CMeasurer::Measure: EOP in single-line control");
  586. eol: // End of current line
  587. if(uiFlags & MEASURE_BREAKATWORD) // Compute count of whitespace
  588. { // chars at EOL
  589. _li._cchWhite = (SHORT)(_li._cch - cchNonWhite);
  590. _li._xWidth = xWidthNonWhite;
  591. }
  592. goto done;
  593. overflow: // Went past max width for line
  594. _li._xWidth -= xAdd;
  595. --iSavedWidths;
  596. _li._xLineOverhang = savedWidths[iSavedWidths & MAX_SAVED_WIDTHS].xLineOverhang;
  597. Advance(cchChunk - cch); // Position *this at overflow
  598. // position
  599. if(uiFlags & MEASURE_BREAKATWORD) // If required, adjust break on
  600. { // word boundary
  601. // We should not have the EOP flag set here. The case to watch out
  602. // for is when we reuse a line that used to have an EOP. It is the
  603. // responsibility of the measurer to clear this flag as appropriate.
  604. Assert(_li._cchEOP == 0);
  605. _li._cchEOP = 0; // Just in case
  606. if(ch == TAB || ch == CELL)
  607. {
  608. // If the last character measured is a tab, leave it on the
  609. // next line to allow tabbing off the end of line as in Word
  610. goto done;
  611. }
  612. LONG cpStop = GetCp(); // Remember current cp
  613. cch = -FindWordBreak(WB_LEFTBREAK, _li._cch+1);
  614. if(cch == 0 && fLastChObj) // If preceding char is an
  615. goto done; // object, put current char
  616. // on next line
  617. Assert(cch >= 0);
  618. if(cch + 1 < _li._cch) // Break char not at BOL
  619. {
  620. ch = _rpTX.GetPrevChar();
  621. if (ch == TAB || ch == CELL) // If break char is a TAB,
  622. { // put it on the next line
  623. cch++; // as in Word
  624. Advance(-1);
  625. }
  626. else if(ch == SOFTHYPHEN)
  627. _li._xWidth += xSoftHyphen;
  628. _li._cch -= cch;
  629. }
  630. else if(cch == _li._cch && cch > 1 &&
  631. _rpTX.GetChar() == ' ') // Blanks all the way back to
  632. { // BOL. Bypass first blank
  633. Advance(1);
  634. cch--;
  635. _li._cch = 1;
  636. }
  637. else // Advance forward to end of
  638. SetCp(cpStop); // measurement
  639. Assert(_li._cch > 0);
  640. // Now search at start of word to figure how many white chars at EOL
  641. if(GetCp() < cchText)
  642. {
  643. pch = GetPch(cch);
  644. cch = 0;
  645. if(ped->TxWordBreakProc((WCHAR *)pch, 0, sizeof(WCHAR), WB_ISDELIMITER, GetCp()))
  646. {
  647. cch = FindWordBreak(WB_RIGHT);
  648. Assert(cch >= 0);
  649. }
  650. _li._cchWhite = (SHORT)cch;
  651. _li._cch += cch;
  652. ch = GetChar();
  653. if(IsASCIIEOP(ch)) // skip *only* 1 EOP -jOn
  654. {
  655. if(ch == CR)
  656. _li._bFlags |= fliHasEOP;
  657. _li._cchEOP = (BYTE)AdvanceCRLF();
  658. _li._cch += _li._cchEOP;
  659. goto done;
  660. }
  661. }
  662. i = cpStop - GetCp();
  663. if(i)
  664. {
  665. if(i > 0)
  666. i += _li._cchWhite;
  667. if(i > 0 && i < iSavedWidths && i < MAX_SAVED_WIDTHS)
  668. {
  669. while (i-- > 0)
  670. {
  671. iSavedWidths = (iSavedWidths - 1) & MAX_SAVED_WIDTHS;
  672. _li._xWidth -= savedWidths[iSavedWidths].width;
  673. }
  674. iSavedWidths = (iSavedWidths - 1) & MAX_SAVED_WIDTHS;
  675. _li._xLineOverhang = savedWidths[iSavedWidths].xLineOverhang;
  676. _li._yHeight = savedWidths[iSavedWidths].yHeight;
  677. _li._yDescent = savedWidths[iSavedWidths].yDescent;
  678. }
  679. else
  680. {
  681. // Need to recompute width from scratch.
  682. _li._xWidth = -1;
  683. lRet = MRET_NOWIDTH;
  684. }
  685. }
  686. else
  687. {
  688. // i == 0 means that we are breaking on the first letter in a word.
  689. // Therefore, we want to set the width to the total non-white space
  690. // calculated so far because that does not include the size of the
  691. // character that caused the break nor any of the white space
  692. // preceeding the character that caused the break.
  693. if(!fHeightChange)
  694. _li._xWidth = xWidthNonWhite;
  695. else
  696. {
  697. // Need to recompute from scratch so that we can get the
  698. // correct height for the control
  699. _li._xWidth = -1;
  700. lRet = MRET_NOWIDTH;
  701. }
  702. }
  703. }
  704. done:
  705. _xAddLast = xAdd;
  706. if(!_li._yHeight) // If no height yet, use
  707. CheckLineHeight(); // default height
  708. AdjustLineHeight();
  709. return lRet;
  710. }
  711. /*
  712. * CMeasurer::GetCcsFontFallback
  713. *
  714. * @mfunc
  715. * Create the fallback font cache for given CF
  716. */
  717. CCcs* CMeasurer::GetCcsFontFallback (const CCharFormat *pCF)
  718. {
  719. CCharFormat CF = *pCF;
  720. CCcs* pccs = NULL;
  721. SHORT iDefHeight;
  722. bool fr = W32->GetPreferredFontInfo(GetCodePage(CF._bCharSet),
  723. GetPed()->fUseUIFont() ? true : false, CF._iFont,
  724. (BYTE&)iDefHeight, CF._bPitchAndFamily);
  725. if (fr)
  726. pccs = GetCcs(&CF); // create fallback font cache entry
  727. return pccs;
  728. }
  729. /*
  730. * CMeasurer::ApplyFontCache (fFallback)
  731. *
  732. * @mfunc
  733. * Apply a new font cache on the fly (leave backing store intact)
  734. */
  735. CCcs* CMeasurer::ApplyFontCache (
  736. BOOL fFallback)
  737. {
  738. if (_fFallback ^ fFallback)
  739. {
  740. CCcs* pccs = fFallback ? GetCcsFontFallback(GetCF()) : GetCcs(GetCF());
  741. if (pccs)
  742. {
  743. if (_pccs)
  744. _pccs->Release();
  745. _pccs = pccs;
  746. _fFallback = fFallback;
  747. }
  748. }
  749. return _pccs;
  750. }
  751. /*
  752. * CMeasurer::GetCcs
  753. *
  754. * @mfunc
  755. * Wrapper around font cache's GetCCcs function
  756. * We use a NULL DC unless the device is a printer.
  757. */
  758. CCcs* CMeasurer::GetCcs(const CCharFormat *pCF)
  759. {
  760. HDC hdc = NULL;
  761. if (_fTarget)
  762. {
  763. if (_pddReference->_hdc && _dtRef == DT_RASPRINTER)
  764. hdc = _pddReference->_hdc;
  765. }
  766. else if (_pdp->_hdc && _dtPres == DT_RASPRINTER)
  767. hdc = _pdp->_hdc;
  768. return fc().GetCcs(pCF, _fTarget ? _dyrInch : _dypInch, hdc,
  769. _fGlyphing && _dtRef == DT_RASPRINTER);
  770. }
  771. /*
  772. * CMeasurer::CheckLineHeight()
  773. *
  774. * @mfunc
  775. * If no height yet, use default height
  776. */
  777. void CMeasurer::CheckLineHeight()
  778. {
  779. CCcs *pccs = GetCcs(GetPed()->GetCharFormat(-1));
  780. _li._yHeight = pccs->_yHeight;
  781. _li._yDescent = pccs->_yDescent;
  782. SHORT yFEAdjust = pccs->AdjustFEHeight(fAdjustFELineHt());
  783. if (yFEAdjust)
  784. {
  785. _li._yHeight += (yFEAdjust << 1);
  786. _li._yDescent += yFEAdjust;
  787. }
  788. pccs->Release();
  789. }
  790. /*
  791. * CMeasurer::Check_pccs()
  792. *
  793. * @mfunc
  794. * Check if new character format run or whether we don't yet have a font
  795. *
  796. * @rdesc
  797. * Current CCcs *
  798. *
  799. * @devnote
  800. * The calling chain must be protected by a CLock, since this present
  801. * routine access the global (shared) FontCache facility.
  802. */
  803. CCcs *CMeasurer::Check_pccs(
  804. BOOL fBullet)
  805. {
  806. if(fBullet)
  807. {
  808. if(_pccs) // Release old Format cache
  809. _pccs->Release();
  810. _pccs = GetCcsBullet(NULL);
  811. _iFormat = -10; // Be sure to reset font next time
  812. return _pccs;
  813. }
  814. const CCharFormat *pCF = GetCF();
  815. if(FormatIsChanged())
  816. {
  817. // New CF run or format for this line not yet initialized
  818. ResetCachediFormat();
  819. if(_pccs) // Release old Format cache
  820. _pccs->Release();
  821. _pccs = GetCcs(pCF);
  822. _fFallback = 0;
  823. if(!_pccs)
  824. {
  825. //FUTURE (keithcu) If this fails, just dig up the first pccs you can find
  826. AssertSz(FALSE, "CMeasurer::Measure could not get _pccs");
  827. return NULL;
  828. }
  829. }
  830. // NOTE: Drawing with a dotted pen on the screen and in a
  831. // compatible bitmap does not seem to match on some hardware.
  832. // If at some future point we do a better job of drawing the
  833. // dotted underline, this statement block can be removed.
  834. if(CFU_UNDERLINEDOTTED == pCF->_bUnderlineType)
  835. {
  836. // We draw all dotted underline lines off screen to get
  837. // a consistent display of the dotted line.
  838. _li._bFlags |= fliUseOffScreenDC;
  839. }
  840. _li._xLineOverhang = _pccs->_xOverhang;
  841. return _pccs;
  842. }
  843. /*
  844. * CMeasurer::AdjustLineHeight()
  845. *
  846. * @mfunc
  847. * Adjust for space before/after and line spacing rules.
  848. * No effect for plain text.
  849. *
  850. * @future
  851. * Base multiple line height calculations on largest font height rather
  852. * than on line height (_yHeight), since the latter may be unduly large
  853. * due to embedded objects. Word does this correctly.
  854. */
  855. void CMeasurer::AdjustLineHeight()
  856. {
  857. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CMeasurer::AdjustLineHeight");
  858. if(!IsRich() || IsInOutlineView()) // Plain text and outline mode
  859. return; // don't use special line
  860. // spacings
  861. const CParaFormat * pPF = _pPF;
  862. DWORD dwRule = pPF->_bLineSpacingRule;
  863. LONG dyAfter = 0; // Default no space after
  864. LONG dyBefore = 0; // Default no space before
  865. LONG dySpacing = pPF->_dyLineSpacing;
  866. LONG yHeight = LYtoDY(dySpacing);
  867. LONG yAscent = _li._yHeight - _li._yDescent;
  868. if(_li._bFlags & fliFirstInPara)
  869. dyBefore = LYtoDY(pPF->_dySpaceBefore); // Space before paragraph
  870. AssertSz(dyBefore >= 0, "CMeasurer::AdjustLineHeight - bogus value for dyBefore");
  871. if(yHeight < 0) // Negative heights mean use
  872. _li._yHeight = (SHORT)(-yHeight); // the magnitude exactly
  873. else if(dwRule) // Line spacing rule is active
  874. {
  875. switch (dwRule)
  876. {
  877. case tomLineSpace1pt5:
  878. dyAfter = _li._yHeight >> 1; // Half-line space after
  879. break; // (per line)
  880. case tomLineSpaceDouble:
  881. dyAfter = _li._yHeight; // Full-line space after
  882. break; // (per line)
  883. case tomLineSpaceAtLeast:
  884. if(_li._yHeight >= yHeight)
  885. break;
  886. // Fall thru to space exactly
  887. case tomLineSpaceExactly:
  888. _li._yHeight = (SHORT)max(yHeight, 1);
  889. break;
  890. case tomLineSpaceMultiple: // Multiple-line space after
  891. // Prevent dyAfter from being negative because dySpacing is small - a-rsail
  892. if (dySpacing < 20)
  893. dySpacing = 20;
  894. dyAfter = (_li._yHeight*dySpacing)/20 // (20 units per line)
  895. - _li._yHeight;
  896. }
  897. }
  898. if(_li._bFlags & fliHasEOP)
  899. dyAfter += LYtoDY(pPF->_dySpaceAfter); // Space after paragraph end
  900. // Add in space before/after
  901. if (dyAfter < 0)
  902. {
  903. // Overflow - since we forced dySpacing to 20 above, the
  904. // only reason for a negative is overflow. In case of overflow,
  905. // we simply force the value to the max and then fix the
  906. // other resulting overflows.
  907. dyAfter = LONG_MAX;
  908. }
  909. AssertSz((dyBefore >= 0), "CMeasurer::AdjustLineHeight - invalid before");
  910. _li._yHeight = (SHORT)(_li._yHeight + dyBefore + dyAfter);
  911. if (_li._yHeight < 0)
  912. {
  913. // Overflow!
  914. // The reason for the -2 is then we don't have to worry about
  915. // overflow in the table check.
  916. _li._yHeight = SHRT_MAX - 2;
  917. }
  918. _li._yDescent = (SHORT)(_li._yDescent + dyAfter);
  919. if (_li._yDescent < 0)
  920. {
  921. // Overflow in descent
  922. AssertSz(_li._yHeight == SHRT_MAX - 2, "Descent overflowed when height didn't");
  923. // Allow old ascent
  924. _li._yDescent = SHRT_MAX - 2 - yAscent;
  925. AssertSz(_li._yDescent >= 0, "descent adjustment < 0");
  926. }
  927. if(_pPF->InTable())
  928. {
  929. _li._yHeight++;
  930. if(!_li._fNextInTable)
  931. {
  932. _li._yHeight++;
  933. _li._yDescent++;
  934. }
  935. }
  936. AssertSz((_li._yHeight >= 0) && (_li._yDescent >= 0),
  937. "CMeasurer::AdjustLineHeight - invalid line heights");
  938. }
  939. /*
  940. * CMeasurer::MeasureLeftIndent()
  941. *
  942. * @mfunc
  943. * Compute and return left indent of line in device units
  944. *
  945. * @rdesc
  946. * Left indent of line in device units
  947. *
  948. * @comm
  949. * Plain text is sensitive to StartIndent and RightIndent settings,
  950. * but usually these are zero for plain text.
  951. */
  952. LONG CMeasurer::MeasureLeftIndent()
  953. {
  954. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CMeasurer::MeasureLeftIndent");
  955. AssertSz(_pPF != NULL, "CMeasurer::MeasureLeftIndent _pPF not set!");
  956. LONG xLeft = _pPF->_dxStartIndent; // Use logical units
  957. // up to return
  958. if(IsRich())
  959. {
  960. LONG dxOffset = _pPF->_dxOffset;
  961. BOOL fFirstInPara = _li._bFlags & fliFirstInPara;
  962. if(IsInOutlineView())
  963. {
  964. xLeft = lDefaultTab/2 * (_pPF->_bOutlineLevel + 1);
  965. if(!fFirstInPara)
  966. dxOffset = 0;
  967. }
  968. if(fFirstInPara)
  969. {
  970. if(_pPF->_wNumbering && !_pPF->IsNumberSuppressed())// Add offset to text
  971. { // on first line
  972. LONG dx = DXtoLX(MeasureBullet()); // Use max of bullet
  973. dx = max(dx, _pPF->_wNumberingTab); // width, numbering tab,
  974. dxOffset = max(dxOffset, dx); // and para offset
  975. }
  976. else if(_pPF->InTable()) // For tables, need to
  977. xLeft += dxOffset; // add in trgaph twice
  978. // since dxStartIndent
  979. else // subtracts one
  980. dxOffset = 0;
  981. }
  982. xLeft += dxOffset;
  983. }
  984. // FUTURE: tables extending to the left of the left margin will be clipped
  985. // accordingly on the left. We could move the table to the right, but
  986. // then we need to move tabs to the right as well (include out parm with
  987. // amount of negative left indent. Ideally we may want to enable a horiz
  988. // scroll bar able to shift to the left of the left margin for this case
  989. // as in Word.
  990. if(!_pPF->InTable() && xLeft <= 0)
  991. return 0;
  992. return LXtoDX(xLeft);
  993. }
  994. /*
  995. * CMeasurer::HitTest(x)
  996. *
  997. * @mfunc
  998. * Return HITTEST for displacement x in this line. Can't be specific
  999. * about text area (_xLeft to _xLeft + _xWidth), since need to measure
  1000. * to get appropriate cp (done elsewhere)
  1001. *
  1002. * @rdesc
  1003. * HITTEST for a displacement x in this line
  1004. */
  1005. HITTEST CMeasurer::HitTest(
  1006. LONG x) //@parm Displacement to test hit
  1007. {
  1008. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CMeasurer::HitTest");
  1009. UpdatePF();
  1010. LONG u = UFromX(x);
  1011. if(u < 0)
  1012. return HT_LeftOfText;
  1013. if(u > _li._xLeft + _li._xWidth)
  1014. return HT_RightOfText;
  1015. if(u >= _li._xLeft) // Caller can refine this
  1016. return HT_Text; // with CLine::CchFromXpos()
  1017. if(IsRich() && (_li._bFlags & fliFirstInPara))
  1018. {
  1019. _pPF = GetPF();
  1020. LONG dx;
  1021. if(_pPF->_wNumbering)
  1022. {
  1023. // Doesn't handle case where Bullet is wider than following dx
  1024. dx = LXtoDX(max(_pPF->_dxOffset, _pPF->_wNumberingTab));
  1025. if(u >= _li._xLeft - dx)
  1026. return HT_BulletArea;
  1027. }
  1028. if(IsInOutlineView())
  1029. {
  1030. dx = LXtoDX(lDefaultTab/2 * _pPF->_bOutlineLevel);
  1031. if(u >= dx && u < dx + (_pPF->_bOutlineLevel & 1
  1032. ? LXtoDX(lDefaultTab/2) : _pdp->Zoom(BITMAP_WIDTH_HEADING)))
  1033. {
  1034. return HT_OutlineSymbol;
  1035. }
  1036. }
  1037. }
  1038. return HT_LeftOfText;
  1039. }
  1040. /*
  1041. * CMeasurer::MeasureRightIndent()
  1042. *
  1043. * @mfunc
  1044. * Compute and return right indent of line in device units
  1045. *
  1046. * @rdesc
  1047. * right indent of line in device units
  1048. *
  1049. * @comm
  1050. * Plain text is sensitive to StartIndent and RightIndent settings,
  1051. * but usually these are zero for plain text.
  1052. */
  1053. LONG CMeasurer::MeasureRightIndent()
  1054. {
  1055. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CMeasurer::MeasureRightIndent");
  1056. return LXtoDX(max(_pPF->_dxRightIndent, 0));
  1057. }
  1058. /*
  1059. * CMeasurer::MeasureTab()
  1060. *
  1061. * @mfunc
  1062. * Computes and returns the width from the current position to the
  1063. * next tab stop (in device units).
  1064. */
  1065. LONG CMeasurer::MeasureTab(unsigned ch)
  1066. {
  1067. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CMeasurer::MeasureTab");
  1068. LONG xCur = _li._xWidth + MeasureLeftIndent();
  1069. const CParaFormat * pPF = _pPF;
  1070. LONG cTab = pPF->_bTabCount;
  1071. LONG dxDefaultTab = lDefaultTab;
  1072. LONG dxIndent = LXtoDX(pPF->_dxStartIndent + pPF->_dxOffset);
  1073. LONG dxOffset = pPF->_dxOffset;
  1074. LONG dxOutline = 0;
  1075. BOOL fInTable = pPF->InTable();
  1076. LONG h = 0;
  1077. LONG xT;
  1078. LONG xTab;
  1079. AssertSz(cTab >= 0 || cTab <= MAX_TAB_STOPS,
  1080. "CMeasurer::MeasureTab: illegal tab count");
  1081. if(fInTable)
  1082. {
  1083. h = LXtoDX(dxOffset);
  1084. dxOffset = 0;
  1085. }
  1086. if(IsInOutlineView())
  1087. dxOutline = lDefaultTab/2 * (pPF->_bOutlineLevel + 1);
  1088. if(cTab && (!fInTable || ch == CELL)) // Use default TAB for TAB in
  1089. { // table
  1090. const LONG *pl = pPF->GetTabs();
  1091. for(xTab = 0; cTab--; pl++) // Try explicit tab stops 1st
  1092. {
  1093. xT = GetTabPos(*pl) + dxOutline; // (2 most significant nibbles
  1094. xT = LXtoDX(xT); // are for type/style)
  1095. if(xT > MaxWidth()) // Ignore tabs wider than display
  1096. break;
  1097. if(xT + h > xCur) // Allow text in table cell to
  1098. { // move into cell gap (h > 0)
  1099. if(dxOffset > 0 && xT < dxIndent)// Explicit tab in a hanging
  1100. return xT - xCur; // indent takes precedence
  1101. xTab = xT;
  1102. break;
  1103. }
  1104. }
  1105. if(dxOffset > 0 && xCur < dxIndent) // If no tab before hanging
  1106. return dxIndent - xCur; // indent, tab to indent
  1107. if(xTab) // Else use tab position
  1108. {
  1109. if(fInTable)
  1110. {
  1111. xTab += h;
  1112. if(cTab) // Don't include cell gap in
  1113. xTab += h; // last cell
  1114. if(IsInOutlineView() && cTab < pPF->_bTabCount)
  1115. xTab += h;
  1116. }
  1117. return xTab - xCur;
  1118. }
  1119. }
  1120. dxDefaultTab = GetTabPos(GetPed()->GetDefaultTab());
  1121. AssertSz(dxDefaultTab > 0, "CMeasurer::MeasureTab: Default tab is bad");
  1122. dxDefaultTab = LXtoDX(dxDefaultTab);
  1123. dxDefaultTab = max(dxDefaultTab, 1); // Don't ever divide by 0
  1124. return dxDefaultTab - xCur%dxDefaultTab; // Round up to nearest
  1125. }
  1126. /*
  1127. * CMeasurer::MeasureLineShift ()
  1128. *
  1129. * @mfunc
  1130. * Computes and returns the line x shift due to alignment
  1131. *
  1132. * @comm
  1133. * Plain text is sensitive to StartIndent and RightIndent settings,
  1134. * but usually these are zero for plain text.
  1135. */
  1136. LONG CMeasurer::MeasureLineShift()
  1137. {
  1138. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CMeasurer::MeasureLineShift");
  1139. WORD wAlignment = _pPF->_bAlignment;
  1140. LONG xShift;
  1141. if (IsInOutlineView() ||
  1142. (wAlignment != PFA_RIGHT && wAlignment != PFA_CENTER))
  1143. {
  1144. return 0;
  1145. }
  1146. // Normal view with center or flush-right para. Move right accordingly
  1147. xShift = _pdp->GetMaxPixelWidth() - _li._xLeft - MeasureRightIndent() -
  1148. dxCaret - _li._xLineOverhang - _li._xWidth;
  1149. xShift = max(xShift, 0); // Don't allow alignment to go < 0
  1150. // Can happen with a target device
  1151. if(wAlignment == PFA_CENTER)
  1152. xShift /= 2;
  1153. return xShift;
  1154. }
  1155. /*
  1156. * CMeasurer::MeasureBullet()
  1157. *
  1158. * @mfunc
  1159. * Computes bullet/numbering dimensions
  1160. *
  1161. * @rdesc
  1162. * return bullet/numbering string width
  1163. */
  1164. LONG CMeasurer::MeasureBullet()
  1165. {
  1166. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CMeasurer::MeasureBullet");
  1167. CCharFormat CF;
  1168. CCcs *pccs = GetCcsBullet(&CF);
  1169. LONG xWidth = 0;
  1170. if(pccs)
  1171. {
  1172. WCHAR szBullet[CCHMAXNUMTOSTR];
  1173. GetBullet(szBullet, pccs, &xWidth);
  1174. RecalcLineHeight(pccs, &CF);
  1175. pccs->Release();
  1176. }
  1177. return xWidth;
  1178. }
  1179. /*
  1180. * CMeasurer::GetBullet(pch, pccs, pxWidth)
  1181. *
  1182. * @mfunc
  1183. * Computes bullet/numbering string, string length, and width
  1184. *
  1185. * @rdesc
  1186. * return bullet/numbering string length
  1187. */
  1188. LONG CMeasurer::GetBullet(
  1189. WCHAR *pch, //@parm Bullet string to receive bullet text
  1190. CCcs *pccs, //@parm CCcs to use
  1191. LONG *pxWidth) //@parm Out parm for bullet width
  1192. {
  1193. Assert(pccs && pch);
  1194. LONG cch = _pPF->NumToStr(pch, _li._bNumber);
  1195. LONG dx;
  1196. LONG i;
  1197. LONG xWidth = 0;
  1198. pch[cch++] = ' '; // Ensure a little extra space
  1199. for(i = cch; i--; xWidth += dx)
  1200. {
  1201. if(!pccs->Include(*pch++, dx))
  1202. {
  1203. TRACEERRSZSC("CMeasurer::GetBullet(): Error filling CCcs", E_FAIL);
  1204. }
  1205. }
  1206. xWidth += pccs->_xUnderhang + pccs->_xOverhang;
  1207. if(pxWidth)
  1208. *pxWidth = xWidth;
  1209. return cch;
  1210. }
  1211. /*
  1212. * CMeasurer::GetCcsBullet(pCFRet)
  1213. *
  1214. * @mfunc
  1215. * Get CCcs for numbering/bullet font. If bullet is suppressed because
  1216. * this isn't the beginning of a paragraph (e.g., previous character is
  1217. * VT or if GetCcs() fails, it returns NULL.
  1218. *
  1219. * @rdesc
  1220. * ptr to bullet CCcs, or NULL (GetCcs() failed or not start of para)
  1221. *
  1222. * @devnote
  1223. * The calling chain must be protected by a CLock, since this present
  1224. * routine access the global (shared) FontCache facility.
  1225. */
  1226. CCcs * CMeasurer::GetCcsBullet(
  1227. CCharFormat *pCFRet) //@parm option character format to return
  1228. {
  1229. TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "CMeasurer::GetCcsBullet");
  1230. if(!(_li._bFlags & fliFirstInPara))
  1231. return NULL; // Number/bullet suppressed
  1232. CCharFormat CF;
  1233. CCcs * pccs;
  1234. const CCharFormat * pCF;
  1235. CCharFormat * pCFUsed = pCFRet ? pCFRet : &CF;
  1236. // Bullet CF is given by that for EOP in bullet's paragraph.
  1237. CTxtPtr tp(_rpTX);
  1238. CFormatRunPtr rpCF(_rpCF);
  1239. rpCF.AdvanceCp(tp.FindEOP(tomForward));
  1240. rpCF.AdjustBackward();
  1241. pCF = GetPed()->GetCharFormat(rpCF.GetFormat());
  1242. // Construct bullet (or numbering) CCharFormat
  1243. *pCFUsed = *pCF;
  1244. if(_pPF->_wNumbering == PFN_BULLET) // Traditional bullet uses
  1245. { // Symbol font bullet, but...
  1246. pCFUsed->_bCharSet = SYMBOL_CHARSET,
  1247. pCFUsed->_bPitchAndFamily = FF_DONTCARE;
  1248. pCFUsed->_iFont = IFONT_SYMBOL;
  1249. }
  1250. // Since we always cook up bullet character format, no need to cache it
  1251. pccs = GetCcs(pCFUsed);
  1252. #if DEBUG
  1253. if(!pccs)
  1254. {
  1255. TRACEERRSZSC("CMeasurer::GetCcsBullet(): no CCcs", E_FAIL);
  1256. }
  1257. #endif // DEBUG
  1258. return pccs;
  1259. }
  1260. /*
  1261. * CMeasurer::SetNumber(wNumber)
  1262. *
  1263. * @mfunc
  1264. * Store number if numbered paragraph
  1265. */
  1266. void CMeasurer::SetNumber(
  1267. WORD wNumber)
  1268. {
  1269. _pPF = GetPF();
  1270. if(!_pPF->IsListNumbered())
  1271. wNumber = 0;
  1272. else if (!wNumber)
  1273. wNumber = 1;
  1274. _wNumber = wNumber;
  1275. }
  1276. /*
  1277. * CMeasurer::DXtoLX(x), LXtoDX(x), LYtoDY(y)
  1278. *
  1279. * @mfunc
  1280. * Functions that convert from file to pixel coordinates
  1281. *
  1282. * @rdesc
  1283. * Scaled coordinate
  1284. */
  1285. LONG CMeasurer::DXtoLX(LONG x)
  1286. {
  1287. return MulDiv(x, LX_PER_INCH, _fTarget ? _dxrInch : _dxpInch);
  1288. }
  1289. LONG CMeasurer::LXtoDX(LONG x)
  1290. {
  1291. return MulDiv(x, _fTarget ? _dxrInch : _dxpInch, LX_PER_INCH);
  1292. }
  1293. LONG CMeasurer::LYtoDY(LONG y)
  1294. {
  1295. return MulDiv(y, _fTarget ? _dyrInch : _dypInch, LX_PER_INCH);
  1296. }
  1297. LONG CMeasurer::XFromU(LONG u)
  1298. {
  1299. #ifdef LINESERVICES
  1300. if (_pPF->IsRtlPara())
  1301. {
  1302. LONG xCaret = _pdp->IsMain() ? dxCaret : 0, xWidth;
  1303. if (_pdp->GetMaxWidth())
  1304. xWidth = LXtoDX(_pdp->GetMaxWidth());
  1305. else
  1306. xWidth = max(0, _pdp->GetMaxPixelWidth());
  1307. if(!_pdp->GetWordWrap())
  1308. {
  1309. xWidth = max(xWidth, _pdp->GetViewWidth());
  1310. xWidth = max(xWidth, _pdp->GetWidth());
  1311. }
  1312. xWidth -= xCaret;
  1313. POINT ptStart = {xWidth, 0};
  1314. POINTUV pointuv = {u, 0};
  1315. POINT pt;
  1316. LsPointXYFromPointUV(&ptStart, lstflowWS, &pointuv, &pt);
  1317. return pt.x;
  1318. }
  1319. else
  1320. #endif
  1321. return u;
  1322. }
  1323. LONG CMeasurer::UFromX(LONG x)
  1324. {
  1325. #ifdef LINESERVICES
  1326. if (_pPF->IsRtlPara())
  1327. return XFromU(x);
  1328. else
  1329. #endif
  1330. return x;
  1331. }
  1332. /*
  1333. * CMeasurer::GetPols(pme)
  1334. *
  1335. * @mfunc
  1336. * Get ptr to LineServices object. If LineServices not enabled,
  1337. * return NULL. If pme is nonNULL, use it as COls::_pme.
  1338. *
  1339. * @rdesc
  1340. * POLS
  1341. */
  1342. #ifdef LINESERVICES
  1343. extern BOOL g_fNoLS;
  1344. COls *CMeasurer::GetPols(
  1345. CMeasurer **ppme)
  1346. {
  1347. CTxtEdit *ped = GetPed();
  1348. if(ppme) // Default no previous measurer
  1349. *ppme = NULL;
  1350. if(g_fNoLS || !ped->fUseLineServices()) // Not using LineServices
  1351. return NULL;
  1352. if(!g_pols) // Starting up LS:
  1353. g_pols = new COls(); // create new COls
  1354. if(g_pols) // Have the COls
  1355. {
  1356. if(ppme)
  1357. *ppme = g_pols->_pme; // Return current g_pols->_pme
  1358. if(g_pols->Init(this) != NOERROR) // Switch to new one
  1359. {
  1360. delete g_pols;
  1361. g_pols = NULL;
  1362. }
  1363. UpdatePF();
  1364. }
  1365. return g_pols;
  1366. }
  1367. #endif