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

718 lines
22 KiB

  1. #include "dispmain.h"
  2. #include "lsc.h"
  3. #include "lsdnode.h"
  4. #include "lstflow.h"
  5. #include "lsline.h"
  6. #include "lssubl.h"
  7. #include "dispul.h"
  8. #include "lstfset.h"
  9. #include "lssubset.h"
  10. #include "dispmisc.h"
  11. #include "dispi.h"
  12. #include "dninfo.h"
  13. #include "memory.h"
  14. #include "port.h"
  15. static LSERR DisplayDnode(PLSC plsc, PLSDNODE pdn, const POINT* pptOrg, POINTUV pt,
  16. UINT kdispmode, LSTFLOW lstflow, const RECT* prectClip,
  17. BOOL fDrawStrike, BOOL fDrawUnderline, long upLimUnderline);
  18. static LSERR ShadeSubline(PLSSUBL plssubl, const POINT* pptOrg, UINT kdispmode,
  19. const RECT* prectClip, long upLimUnderline, long upLeftIndent);
  20. static LSERR DrawBorders(PLSSUBL plssubl, const POINT* pptOrg, UINT kdispmode,
  21. const RECT* prectClip, long upLimUnderline, long upLeftIndent);
  22. static long GetDupUnderline(long up, long dup, long upLimUnderline);
  23. static BOOL FGetNeighboringOpeningBorder(PLSDNODE pdnClosingBorder, PLSDNODE pdnNext, POINTUV* pptStart,
  24. LSCP cpLim, LSTFLOW lstflowMain,
  25. PLSDNODE* ppdnOpeningBorder, POINTUV* pptStartOpeningBorder);
  26. #define FIsDnodeToShade(pdn, cpLim) (FDnodeBeforeCpLim(pdn, cpLim) && FIsDnodeReal(pdn) && \
  27. !(pdn)->u.real.lschp.fInvisible && (pdn)->u.real.lschp.fShade)
  28. #define UpdateMaximum(a,b) if ((a) < (b)) (a) = (b); else
  29. #define FIsDnodeOpeningBorder(pdn, lstflowMain) ((pdn)->fOpenBorder ^ ((pdn)->plssubl->lstflow != (lstflowMain)))
  30. #define FIsDnodeClosingBorder(pdn, lstflowMain) (!FIsDnodeOpeningBorder(pdn, lstflowMain))
  31. // %%Function: DisplaySublineCore
  32. // %%Contact: victork
  33. //
  34. //
  35. // Displays subline with shading, striking and underlining, merging consecutive underlined dnodes
  36. //
  37. // Logic to select underlining method:
  38. //
  39. // If (metrics_are_good)
  40. // Draw_by_fnDrawUnderline;
  41. // else
  42. // if (There_is_merging_going)
  43. // Draw_by_pfnDrawUnderlineAsText;
  44. // else
  45. // Draw_along_with_Display;
  46. LSERR DisplaySublineCore(
  47. PLSSUBL plssubl, /* subline to display */
  48. const POINT* pptOrg, /* (x,y) starting point */
  49. UINT kdispmode, /* transparent or opaque */
  50. const RECT* prectClip, /* clipping rect (x,y) */
  51. long upLimUnderline,
  52. long upLeftIndent)
  53. {
  54. LSERR lserr;
  55. LSCP cpLim = plssubl->cpLimDisplay;
  56. PLSC plsc = plssubl->plsc;
  57. LSTFLOW lstflowMain = plssubl->lstflow;
  58. BOOL fCollectVisual = plsc->plslineDisplay->fCollectVisual;
  59. LSSTRIKEMETRIC lsstrikemetric;
  60. // Underline merge group - normal scenario: we first count dnodes willing to participate
  61. // in merging looking ahead, then draw them, then underline them as a whole
  62. PLSDNODE pdnFirstInGroup = NULL; /* First dnode in Underline merge group */
  63. int cdnodesLeftInGroup = 0; /* these are not displayed yet */
  64. int cdnodesToUnderline = 0; /* these are already displayed */
  65. BOOL fGoodUnderline = fFalse; /* is there metric for UL */
  66. LSULMETRIC lsulmetric; /* merge metric info (if fGoodUnderline) */
  67. long upUnderlineStart = 0; /* Starting point for the group */
  68. BOOL fMergeUnderline = fFalse; /* There is more then one dnode in the group */
  69. BOOL fUnderlineWithDisplay, fStrikeWithDisplay;
  70. POINTUV pt;
  71. PLSDNODE pdn;
  72. FLineValid(plsc->plslineDisplay, plsc); // Assert the display context is valid
  73. Assert(plssubl->plsdnUpTemp == NULL); // against displaying accepted sublines
  74. if (fCollectVisual)
  75. {
  76. CreateDisplayTree(plssubl);
  77. }
  78. if (plsc->plslineDisplay->AggregatedDisplayFlags & fPortDisplayShade)
  79. {
  80. lserr = ShadeSubline(plssubl, pptOrg, kdispmode, prectClip, upLimUnderline, upLeftIndent);
  81. if (lserr != lserrNone) return lserr;
  82. }
  83. pt.u = upLeftIndent;
  84. pt.v = 0;
  85. pdn = AdvanceToFirstDnode(plssubl, lstflowMain, &pt);
  86. while (FDnodeBeforeCpLim(pdn, cpLim))
  87. {
  88. if (pdn->klsdn == klsdnReal && !pdn->u.real.lschp.fInvisible)
  89. {
  90. /* Real dnode */
  91. fStrikeWithDisplay = fFalse;
  92. if (pdn->u.real.lschp.fStrike)
  93. {
  94. BOOL fGoodStrike;
  95. lserr = GetStrikeMetric(plsc, pdn, lstflowMain, &lsstrikemetric, &fGoodStrike);
  96. if (lserr != lserrNone) return lserr;
  97. fStrikeWithDisplay = !fGoodStrike;
  98. }
  99. fUnderlineWithDisplay = fFalse;
  100. if (pdn->u.real.lschp.fUnderline && pt.u < upLimUnderline)
  101. {
  102. /* Node has underline */
  103. if (cdnodesLeftInGroup == 0)
  104. {
  105. /* There are no on-going UL group */
  106. /* Find out how many dnodes will participate in the merge and what metric to use */
  107. lserr = GetUnderlineMergeMetric(plsc, pdn, pt, upLimUnderline, lstflowMain,
  108. cpLim, &lsulmetric, &cdnodesLeftInGroup , &fGoodUnderline);
  109. if (lserr != lserrNone) return lserr;
  110. fMergeUnderline = (cdnodesLeftInGroup > 1);
  111. cdnodesToUnderline = 0;
  112. }
  113. if (!fGoodUnderline)
  114. fUnderlineWithDisplay = !fMergeUnderline;
  115. else
  116. fUnderlineWithDisplay = fFalse;
  117. if (!fUnderlineWithDisplay)
  118. {
  119. if (cdnodesToUnderline == 0)
  120. {
  121. /* Mark starting point of underline merge */
  122. pdnFirstInGroup = pdn;
  123. upUnderlineStart = pt.u;
  124. }
  125. /* Add to pending UL dnode count */
  126. ++cdnodesToUnderline;
  127. }
  128. // current dnode will be displayed shortly - consider it done
  129. --cdnodesLeftInGroup ;
  130. }
  131. lserr = DisplayDnode(plsc, pdn, pptOrg, pt, kdispmode, lstflowMain, prectClip,
  132. fStrikeWithDisplay, fUnderlineWithDisplay, upLimUnderline);
  133. if (lserr != lserrNone) return lserr;
  134. if (pdn->u.real.lschp.fStrike && !fStrikeWithDisplay)
  135. {
  136. lserr = StrikeDnode(plsc, pdn, pptOrg, pt, &lsstrikemetric, kdispmode, prectClip,
  137. upLimUnderline, lstflowMain);
  138. if (lserr != lserrNone) return lserr;
  139. }
  140. /* Draw any pending UL after last dnode in group has been drawn */
  141. if (cdnodesToUnderline != 0 && cdnodesLeftInGroup == 0)
  142. {
  143. lserr = DrawUnderlineMerge(plsc, pdnFirstInGroup, pptOrg,
  144. cdnodesToUnderline, upUnderlineStart, fGoodUnderline, &lsulmetric,
  145. kdispmode, prectClip, upLimUnderline, lstflowMain);
  146. if (lserr != lserrNone) return lserr;
  147. cdnodesToUnderline = 0;
  148. }
  149. }
  150. pdn = AdvanceToNextDnode(pdn, lstflowMain, &pt);
  151. }
  152. if (fCollectVisual)
  153. {
  154. // call display method for submitting dnodes
  155. pt.u = upLeftIndent;
  156. pt.v = 0;
  157. pdn = AdvanceToFirstSubmittingDnode(plssubl, lstflowMain, &pt);
  158. while (FDnodeBeforeCpLim(pdn, cpLim))
  159. {
  160. lserr = DisplayDnode(plsc, pdn, pptOrg, pt, kdispmode, lstflowMain, prectClip,
  161. fFalse, fFalse, upLimUnderline);
  162. if (lserr != lserrNone) return lserr;
  163. pdn = AdvanceToNextSubmittingDnode(pdn, lstflowMain, &pt);
  164. }
  165. }
  166. if (plsc->plslineDisplay->AggregatedDisplayFlags & fPortDisplayBorder)
  167. {
  168. lserr = DrawBorders(plssubl, pptOrg, kdispmode, prectClip, upLimUnderline, upLeftIndent);
  169. if (lserr != lserrNone) return lserr;
  170. }
  171. if (fCollectVisual)
  172. {
  173. // destroy display tree
  174. DestroyDisplayTree(plssubl);
  175. }
  176. return lserrNone;
  177. }
  178. // %%Function: DisplayDnode
  179. // %%Contact: victork
  180. static LSERR DisplayDnode(PLSC plsc, PLSDNODE pdn, const POINT* pptOrg, POINTUV pt,
  181. UINT kdispmode, LSTFLOW lstflowMain, const RECT* prectClip,
  182. BOOL fDrawStrike, BOOL fDrawUnderline, long upLimUnderline)
  183. {
  184. PDOBJ pdobj;
  185. DISPIN dispin;
  186. pdobj = pdn->u.real.pdobj;
  187. dispin.plschp = &(pdn->u.real.lschp);
  188. dispin.plsrun = pdn->u.real.plsrun;
  189. dispin.kDispMode = kdispmode;
  190. dispin.lstflow = pdn->plssubl->lstflow;
  191. dispin.prcClip = (RECT*) prectClip;
  192. dispin.fDrawUnderline = fDrawUnderline;
  193. dispin.fDrawStrikethrough = fDrawStrike;
  194. dispin.heightsPres = pdn->u.real.objdim.heightsPres;
  195. dispin.dup = pdn->u.real.dup;
  196. dispin.dupLimUnderline = GetDupUnderline(pt.u, dispin.dup, upLimUnderline);
  197. if (dispin.lstflow != lstflowMain)
  198. {
  199. // Dnode lstflow is opposite to lstflowMain - get real starting point
  200. pt.u = pt.u + dispin.dup - 1;
  201. // Partial underlining can only happen on the top level
  202. Assert(dispin.dupLimUnderline == 0 || dispin.dupLimUnderline == dispin.dup);
  203. }
  204. pt.v += pdn->u.real.lschp.dvpPos;
  205. LsPointXYFromPointUV(pptOrg, lstflowMain, &pt, &(dispin.ptPen));
  206. return (*plsc->lsiobjcontext.rgobj[pdn->u.real.lschp.idObj].lsim.pfnDisplay)(pdobj, &dispin);
  207. }
  208. // %%Function: ShadeSubline
  209. // %%Contact: victork
  210. LSERR ShadeSubline(PLSSUBL plssubl, /* subline to shade */
  211. const POINT* pptOrg, /* (x,y) starting point */
  212. UINT kdispmode, /* transparent or opaque */
  213. const RECT* prectClip, /* clipping rect (x,y) */
  214. long upLimUnderline,
  215. long upLeftIndent)
  216. {
  217. LSERR lserr;
  218. LSCP cpLim = plssubl->cpLimDisplay;
  219. PLSC plsc = plssubl->plsc;
  220. PLSLINE plsline = plsc->plslineDisplay;
  221. LSTFLOW lstflowMain = plssubl->lstflow;
  222. POINTUV pt;
  223. PLSDNODE pdn;
  224. HEIGHTS heightsLineWithAddedSpace;
  225. HEIGHTS heightsLineWithoutAddedSpace;
  226. OBJDIM objdimSubline;
  227. POINT ptStart;
  228. PLSRUN plsrunFirst, plsrunPrevious;
  229. long upStart;
  230. long dupInclTrail, dupExclTrail;
  231. HEIGHTS heightsRunsInclTrail;
  232. HEIGHTS heightsRunsExclTrail;
  233. BOOL fInterruptShading;
  234. BOOL fCollectVisual = plsc->plslineDisplay->fCollectVisual;
  235. heightsLineWithAddedSpace.dvAscent = plsline->dvpAbove + plsline->lslinfo.dvpAscent;
  236. heightsLineWithAddedSpace.dvDescent = plsline->dvpBelow + plsline->lslinfo.dvpDescent;
  237. heightsLineWithAddedSpace.dvMultiLineHeight = dvHeightIgnore;
  238. heightsLineWithoutAddedSpace.dvAscent = plsline->lslinfo.dvpAscent;
  239. heightsLineWithoutAddedSpace.dvDescent = plsline->lslinfo.dvpDescent;
  240. heightsLineWithoutAddedSpace.dvMultiLineHeight = dvHeightIgnore;
  241. lserr = LssbGetObjDimSubline(plssubl, &lstflowMain, &objdimSubline);
  242. if (lserr != lserrNone) return lserr;
  243. if (fCollectVisual)
  244. {
  245. // shade submitting dnodes - pretend they are on the top level, no merging for them
  246. pt.u = upLeftIndent;
  247. pt.v = 0;
  248. pdn = AdvanceToFirstSubmittingDnode(plssubl, lstflowMain, &pt);
  249. while (FDnodeBeforeCpLim(pdn, cpLim))
  250. {
  251. if (FIsDnodeToShade(pdn, cpLim))
  252. {
  253. LsPointXYFromPointUV(pptOrg, lstflowMain, &pt, &ptStart);
  254. dupInclTrail = pdn->u.real.dup;
  255. dupExclTrail = GetDupUnderline(pt.u, dupInclTrail, upLimUnderline);
  256. lserr = (*plsc->lscbk.pfnShadeRectangle)(plsc->pols, pdn->u.real.plsrun, &ptStart,
  257. &heightsLineWithAddedSpace, &heightsLineWithoutAddedSpace,
  258. &(objdimSubline.heightsPres),
  259. &(pdn->u.real.objdim.heightsPres), &(pdn->u.real.objdim.heightsPres),
  260. dupExclTrail, dupInclTrail,
  261. lstflowMain, kdispmode, prectClip);
  262. if (lserr != lserrNone) return lserr;
  263. }
  264. pdn = AdvanceToNextSubmittingDnode(pdn, lstflowMain, &pt);
  265. }
  266. }
  267. pt.u = upLeftIndent;
  268. pt.v = 0;
  269. pdn = AdvanceToFirstDnode(plssubl, lstflowMain, &pt);
  270. while (FDnodeBeforeCpLim(pdn, cpLim) && !FIsDnodeToShade(pdn, cpLim))
  271. {
  272. pdn = AdvanceToNextDnode(pdn, lstflowMain, &pt);
  273. }
  274. // next loop will do one shading merge at a run
  275. while (FDnodeBeforeCpLim(pdn, cpLim))
  276. {
  277. // pdn is the first dnode to participate in the shade merge
  278. // initialize the merge with this dnode data
  279. LsPointXYFromPointUV(pptOrg, lstflowMain, &pt, &ptStart);
  280. plsrunFirst = pdn->u.real.plsrun;
  281. upStart = pt.u;
  282. heightsRunsInclTrail = pdn->u.real.objdim.heightsPres;
  283. // What should we have in heightsRunsExclTrail if all shading is in trailing spaces?
  284. // I decided to put heights of the first run there for convenience sake
  285. // Client can check for dupExclTrail == 0.
  286. heightsRunsExclTrail = heightsRunsInclTrail;
  287. // We will now append to the merge as many dnodes as possible
  288. // The loop will stop when dnode doesn't need to be shaded - while condition
  289. // or if callback says two dnodes are not to be shaded together - break inside
  290. plsrunPrevious = pdn->u.real.plsrun;
  291. pdn = AdvanceToNextDnode(pdn, lstflowMain, &pt);
  292. while (FIsDnodeToShade(pdn, cpLim))
  293. {
  294. lserr = (*plsc->lscbk.pfnFInterruptShade)(plsc->pols, plsrunPrevious,pdn->u.real.plsrun,
  295. &fInterruptShading);
  296. if (lserr != lserrNone) return lserr;
  297. if (fInterruptShading)
  298. {
  299. break;
  300. }
  301. plsrunPrevious = pdn->u.real.plsrun;
  302. UpdateMaximum(heightsRunsInclTrail.dvAscent, pdn->u.real.objdim.heightsPres.dvAscent);
  303. UpdateMaximum(heightsRunsInclTrail.dvDescent, pdn->u.real.objdim.heightsPres.dvDescent);
  304. UpdateMaximum(heightsRunsInclTrail.dvMultiLineHeight, pdn->u.real.objdim.heightsPres.dvMultiLineHeight);
  305. if (pt.u < upLimUnderline)
  306. {
  307. UpdateMaximum(heightsRunsExclTrail.dvAscent, pdn->u.real.objdim.heightsPres.dvAscent);
  308. UpdateMaximum(heightsRunsExclTrail.dvDescent, pdn->u.real.objdim.heightsPres.dvDescent);
  309. UpdateMaximum(heightsRunsExclTrail.dvMultiLineHeight, pdn->u.real.objdim.heightsPres.dvMultiLineHeight);
  310. }
  311. pdn = AdvanceToNextDnode(pdn, lstflowMain, &pt);
  312. }
  313. // Merge is stopped - time to draw
  314. dupInclTrail = pt.u - upStart;
  315. dupExclTrail = GetDupUnderline(upStart, dupInclTrail, upLimUnderline);
  316. lserr = (*plsc->lscbk.pfnShadeRectangle)(plsc->pols, plsrunFirst, &ptStart,
  317. &heightsLineWithAddedSpace, &heightsLineWithoutAddedSpace,
  318. &(objdimSubline.heightsPres),
  319. &heightsRunsExclTrail, &heightsRunsInclTrail,
  320. dupExclTrail, dupInclTrail,
  321. lstflowMain, kdispmode, prectClip);
  322. if (lserr != lserrNone) return lserr;
  323. // get to the beginning of the next shade merge
  324. while (FDnodeBeforeCpLim(pdn, cpLim) && !FIsDnodeToShade(pdn, cpLim))
  325. {
  326. pdn = AdvanceToNextDnode(pdn, lstflowMain, &pt);
  327. }
  328. }
  329. return lserrNone;
  330. }
  331. // %%Function: GetDupUnderline
  332. // %%Contact: victork
  333. //
  334. // Calculate dup of underlined part (of dnode). Deals with situations when upLimUnderline is
  335. // outside of [upStart, upStart + dup]
  336. static long GetDupUnderline(long upStart, long dup, long upLimUnderline)
  337. {
  338. long dupLimUnderline;
  339. dupLimUnderline = upLimUnderline - upStart;
  340. if (dupLimUnderline >= dup)
  341. {
  342. dupLimUnderline = dup;
  343. }
  344. else if (dupLimUnderline < 0)
  345. {
  346. dupLimUnderline = 0;
  347. }
  348. return dupLimUnderline;
  349. }
  350. // %%Function: DrawBorders
  351. // %%Contact: victork
  352. LSERR DrawBorders(PLSSUBL plssubl,
  353. const POINT* pptOrg, /* (x,y) starting point */
  354. UINT kdispmode, /* transparent or opaque */
  355. const RECT* prectClip, /* clipping rect (x,y) */
  356. long upLimUnderline,
  357. long upLeftIndent)
  358. {
  359. LSERR lserr;
  360. LSCP cpLim = plssubl->cpLimDisplay;
  361. PLSC plsc = plssubl->plsc;
  362. PLSLINE plsline = plsc->plslineDisplay;
  363. LSTFLOW lstflowMain = plssubl->lstflow;
  364. HEIGHTS heightsLineWithAddedSpace;
  365. HEIGHTS heightsLineWithoutAddedSpace;
  366. OBJDIM objdimSubline;
  367. HEIGHTS heightsRuns;
  368. long upStart, dupBorder, dupBordered;
  369. POINT ptStart;
  370. PLSRUN plsrunOpeningBorder, plsrunClosingBorder;
  371. POINTUV pt, ptAfterClosingBorder;
  372. PLSDNODE pdn, pdnPrev, pdnClosingBorder, pdnAfterClosingBorder;
  373. BOOL fClosingOpeningBorderSequenceFound;
  374. PLSDNODE pdnNextOpeningBorder;
  375. POINTUV ptStartNextOpeningBorder;
  376. BOOL fInterruptBorder;
  377. heightsLineWithAddedSpace.dvAscent = plsline->dvpAbove + plsline->lslinfo.dvpAscent;
  378. heightsLineWithAddedSpace.dvDescent = plsline->dvpBelow + plsline->lslinfo.dvpDescent;
  379. heightsLineWithAddedSpace.dvMultiLineHeight = dvHeightIgnore;
  380. heightsLineWithoutAddedSpace.dvAscent = plsline->lslinfo.dvpAscent;
  381. heightsLineWithoutAddedSpace.dvDescent = plsline->lslinfo.dvpDescent;
  382. heightsLineWithoutAddedSpace.dvMultiLineHeight = dvHeightIgnore;
  383. lserr = LssbGetObjDimSubline(plssubl, &lstflowMain, &objdimSubline);
  384. if (lserr != lserrNone) return lserr;
  385. pt.u = upLeftIndent;
  386. pt.v = 0;
  387. pdn = AdvanceToFirstDnode(plssubl, lstflowMain, &pt);
  388. // next loop will draw one border at a run
  389. while (FDnodeBeforeCpLim(pdn, cpLim))
  390. {
  391. // first find an opening border
  392. while (FDnodeBeforeCpLim(pdn, cpLim) && !FIsDnodeBorder(pdn))
  393. {
  394. pdn = AdvanceToNextDnode(pdn, lstflowMain, &pt);
  395. }
  396. if (FDnodeBeforeCpLim(pdn, cpLim))
  397. {
  398. // border is found - it must be an opening one
  399. Assert(FIsDnodeOpeningBorder(pdn, lstflowMain));
  400. // remember the starting point and border width
  401. upStart = pt.u;
  402. LsPointXYFromPointUV(pptOrg, lstflowMain, &pt, &ptStart);
  403. dupBorder = pdn->u.pen.dup;
  404. // take lsrun from the first bordered run
  405. pdn = AdvanceToNextDnode(pdn, lstflowMain, &pt);
  406. Assert(FDnodeBeforeCpLim(pdn, cpLim) && FIsDnodeReal(pdn));
  407. plsrunOpeningBorder = pdn->u.real.plsrun;
  408. // start collecting max run height
  409. heightsRuns = pdn->u.real.objdim.heightsPres;
  410. // now look for an closing border to draw, collecting max run height
  411. // loop will be ended by break
  412. for (;;)
  413. {
  414. // find a border
  415. pdnPrev = NULL;
  416. while (FDnodeBeforeCpLim(pdn, cpLim) && !FIsDnodeBorder(pdn))
  417. {
  418. if (FIsDnodeReal(pdn))
  419. {
  420. UpdateMaximum(heightsRuns.dvAscent, pdn->u.real.objdim.heightsPres.dvAscent);
  421. UpdateMaximum(heightsRuns.dvDescent, pdn->u.real.objdim.heightsPres.dvDescent);
  422. UpdateMaximum(heightsRuns.dvMultiLineHeight, pdn->u.real.objdim.heightsPres.dvMultiLineHeight);
  423. }
  424. pdnPrev = pdn;
  425. pdn = AdvanceToNextDnode(pdn, lstflowMain, &pt);
  426. }
  427. Assert(FDnodeBeforeCpLim(pdn, cpLim));
  428. // border is found - it must be a closing one
  429. // Sequence opening border - closing border is prohibited by formatting
  430. Assert(pdnPrev != NULL);
  431. Assert(FIsDnodeReal(pdnPrev));
  432. Assert(FIsDnodeClosingBorder(pdn, lstflowMain));
  433. Assert(pdn->u.pen.dup == dupBorder);
  434. pdnClosingBorder = pdn;
  435. plsrunClosingBorder = pdnPrev->u.real.plsrun;
  436. pdn = AdvanceToNextDnode(pdn, lstflowMain, &pt);
  437. ptAfterClosingBorder = pt;
  438. pdnAfterClosingBorder = pdn;
  439. // check for the "surplus borders" situation: closing border and opening border of the same
  440. // type brought together by submitting sublines. (Hard to check at formatting time)
  441. // It can be more complicated if there are bordered trailing spaces between the two borders
  442. // (Trailing spaces can happen in the middle of the line in Bidi case). The problem is
  443. // that border is moved away from trailing spaces at SetBreak time. We try to restore
  444. // bordering of trailing spaces when they are in the middle of bordered line below.
  445. fClosingOpeningBorderSequenceFound = FGetNeighboringOpeningBorder(pdnClosingBorder, pdn, &pt,
  446. cpLim, lstflowMain, &pdnNextOpeningBorder, &ptStartNextOpeningBorder);
  447. if (fClosingOpeningBorderSequenceFound)
  448. {
  449. pdn = pdnNextOpeningBorder;
  450. pt = ptStartNextOpeningBorder;
  451. pdn = AdvanceToNextDnode(pdn, lstflowMain, &pt);
  452. Assert(FDnodeBeforeCpLim(pdn, cpLim) && FIsDnodeReal(pdn));
  453. lserr = (*plsc->lscbk.pfnFInterruptBorder)(plsc->pols, plsrunClosingBorder,
  454. pdn->u.real.plsrun, &fInterruptBorder);
  455. if (lserr != lserrNone) return lserr;
  456. if (!fInterruptBorder)
  457. {
  458. // Client decided against interrupting border here. These two border dnodes
  459. // will be ignored. Space reserved for them by formatting will be left empty.
  460. // Continue seeking for closing border starting from pdn
  461. continue;
  462. }
  463. }
  464. // No special situation - we are ready to display
  465. // Well, we are almost ready. Word doesn't normally draw borders in trailing spaces,
  466. // just reserve space for them and leave this space blank. In FE Word, however,
  467. // borders are drawn if underlining of trailing spaces is required.
  468. // We hack in the following way: Borders in trailing area are deleted after formatting.
  469. // If there are a border which opens in text and closes in trailing spaces, it is moved
  470. // to the left to exclude trailing spaces. If the fUnderlineTrailSpacesRM flag is on
  471. // the "moved" border is marked and now have to be displayed up to upLimUnderline.
  472. // Yes, it's bad, it will appear painted over already displayed spaces (queries!) and
  473. // what about a scenario when not all trailing spaces are bordered? We know, we know.
  474. // Word can even get a "negative" border with a negative advance field.
  475. dupBordered = ptAfterClosingBorder.u - upStart;
  476. if (pdnClosingBorder->fBorderMovedFromTrailingArea)
  477. {
  478. Assert(ptAfterClosingBorder.u <= upLimUnderline);
  479. dupBordered = upLimUnderline - upStart;
  480. }
  481. lserr = (*plsc->lscbk.pfnDrawBorder)(plsc->pols, plsrunOpeningBorder, &ptStart,
  482. &heightsLineWithAddedSpace, &heightsLineWithoutAddedSpace,
  483. &(objdimSubline.heightsPres), &heightsRuns,
  484. dupBorder, dupBordered,
  485. lstflowMain, kdispmode, prectClip);
  486. if (lserr != lserrNone) return lserr;
  487. // maybe we peeped ahead checking for the surplus borders - return
  488. pdn = pdnAfterClosingBorder;
  489. pt = ptAfterClosingBorder;
  490. break;
  491. }
  492. }
  493. // Previous border is drawn, start looking for the next one from pdn.
  494. }
  495. return lserrNone;
  496. }
  497. // Find an opening border neighboring pdnClosingBorder broght together by submitting sublines.
  498. // Ignore trailing spaces that lost their borders during SetBreak - their heights will be ignored.
  499. // Input: pdnClosingBorder
  500. // pdnNext - next to pdnClosingBorder (in visual order)
  501. // ptStart - starting poing of pdnNext (in visual order)
  502. // cpLim and lstflowMain
  503. // Output: pdnOpeningBorder and ptStartOpeningBorder
  504. static BOOL FGetNeighboringOpeningBorder(PLSDNODE pdnClosingBorder, PLSDNODE pdnNext, POINTUV* pptStart,
  505. LSCP cpLim, LSTFLOW lstflowMain,
  506. PLSDNODE* ppdnOpeningBorder, POINTUV* pptStartOpeningBorder)
  507. {
  508. PLSDNODE pdn;
  509. POINTUV pt;
  510. pdn = pdnNext;
  511. pt = *pptStart;
  512. // skip spaces that were bordered once
  513. // not sure about spaces, but what else could be skipped?
  514. while (FDnodeBeforeCpLim(pdn, cpLim) && FIsDnodeReal(pdn) && pdn->u.real.lschp.fBorder)
  515. {
  516. pdn = AdvanceToNextDnode(pdn, lstflowMain, &pt);
  517. }
  518. if (!FDnodeBeforeCpLim(pdn, cpLim))
  519. {
  520. return fFalse;
  521. }
  522. // looking for an opening border from another subline
  523. if (FIsDnodeOpeningBorder(pdn, lstflowMain) && pdn->plssubl != pdnClosingBorder->plssubl)
  524. {
  525. *ppdnOpeningBorder = pdn;
  526. *pptStartOpeningBorder = pt;
  527. return fTrue;
  528. }
  529. return fFalse;
  530. }
  531. // N.B.
  532. // Interruption of underlining/shading logic by invisible dnode is OK, because we are sure that no
  533. // underlining/shading is allowed in preprinted forms.