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.

1956 lines
56 KiB

  1. #include <limits.h>
  2. #include "prepdisp.h"
  3. #include "lsc.h"
  4. #include "lsline.h"
  5. #include "lssubl.h"
  6. #include "iobj.h"
  7. #include "lstxtjst.h"
  8. #include "lstxttab.h"
  9. #include "lsgrchnk.h"
  10. #include "posichnk.h"
  11. #include "chnutils.h"
  12. #include "tabutils.h"
  13. #include "lsdnode.h"
  14. #include "zqfromza.h"
  15. #include "lsdevice.h"
  16. #include "lssubset.h"
  17. #include "lsffi.h"
  18. #include "iobjln.h"
  19. #include "txtconst.h"
  20. #include "lskalign.h"
  21. #include "dninfo.h"
  22. typedef enum /* types of TextGroupChunk walls */
  23. {
  24. LineBegin,
  25. LineEnd,
  26. Tab,
  27. Pen,
  28. } KWALL;
  29. typedef struct
  30. {
  31. KWALL kwall; /* wall type */
  32. PLSDNODE pdn; /* tab or pen dnode, PLINEBEGIN for LineBegin */
  33. LSKTAB lsktab; /* if type is Tab - kind of tab */
  34. WCHAR wchCharTab; /* point character if lsktab == lsktChar */
  35. long upTab; /* scaled tab position */
  36. } GrpChnkWall;
  37. static BOOL DnodeHasSublineForMe(PLSDNODE pdn, BOOL fLineCompressed);
  38. static void ScaleDownLevel(PLSSUBL plssubl, BOOL* pfAnySublines, BOOL* pfCollectVisual);
  39. static LSERR SetJustificationForLastGroupChunk(PLSLINE plsline, GrpChnkWall LastWall,
  40. LSKJUST* plskj, LSKALIGN* plskalign);
  41. static LSERR CalcPresAutonumbers(PLSLINE plsline, PLSDNODE* pdnStartMainText);
  42. static void FindWallToCollectSublinesAfter(PLSDNODE pdnFirst, LSCP cpLim, BOOL fLineCompressed, PLSDNODE* ppdnLastWall);
  43. static LSERR GetDistanceToTabPoint(GRCHUNKEXT* pgrchunkext, LSCP cpLim, LSKTAB lsktab, WCHAR wchCharTab,
  44. PLSDNODE pdnFirst, long* pdupToDecimal);
  45. static void WidenNonTextObjects(GRCHUNKEXT* pgrchunkext, long dupToAdd, DWORD cObjects);
  46. static void ConvertAutoTabToPen(PLSLINE plsline, PLSDNODE pdnAutoDecimalTab);
  47. static LSERR CalcPresForDnodeWithSublines(PLSC plsc, PLSDNODE pdn, BOOL fLineCompressed,
  48. LSKJUST lskj, BOOL fLastOnLine);
  49. static LSERR CalcPresChunk(PLSC plsc, PLSDNODE pdnFirst, PLSDNODE pdnLim,
  50. COLLECTSUBLINES CollectGroupChunkPurpose, BOOL fLineCompressed,
  51. LSKJUST lskj, BOOL fLastOnLine);
  52. static void UpdateUpLimUnderline(PLSLINE plsline, long dupTail);
  53. static LSERR PrepareLineForDisplay(PLSLINE plsline);
  54. #define PLINEBEGIN ((void *)(-1))
  55. #define FIsWall(p, cpLim) (!FDnodeBeforeCpLim(p, cpLim) || p->fTab || FIsDnodePen(p))
  56. #define FIsDnodeNormalPen(plsdn) (FIsDnodePen(plsdn) && (!(plsdn)->fAdvancedPen))
  57. #define FCollinearTflows(t1, t2) (((t1) & fUVertical) == ((t2) & fUVertical))
  58. // %%Function: DnodeHasSublineForMe
  59. // %%Contact: victork
  60. //
  61. // Is there relevant subline in this dnode?
  62. static BOOL DnodeHasSublineForMe(PLSDNODE pdn, BOOL fLineCompressed)
  63. {
  64. BOOL fSublineFound = fFalse;
  65. if (FIsDnodeReal(pdn) && pdn->u.real.pinfosubl != NULL)
  66. {
  67. if (pdn->u.real.pinfosubl->fUseForCompression && fLineCompressed)
  68. {
  69. fSublineFound = fTrue;
  70. }
  71. if (pdn->u.real.pinfosubl->fUseForJustification && !fLineCompressed)
  72. {
  73. fSublineFound = fTrue;
  74. }
  75. }
  76. return fSublineFound;
  77. }
  78. // %%Function: ScaleDownLevel
  79. // %%Contact: victork
  80. //
  81. /*
  82. * Scales all non-text objects on the level(s).
  83. *
  84. * If the level (meaning subline) contains dnode(s) which submitted sublines for compression
  85. * or expansion, ScaleDownLevel reports the fact and calls itself for submitted sublines.
  86. * This strategy relyes on the fact that ScaleDownLevel is idempotent procedure. Some sublines
  87. * will be scaled down twice - let that be.
  88. *
  89. * Two additional questions are answered - whether there are some submitted sublines and
  90. * whether there is a reason go VisualLine (underlining, shading, borders on lower levels).
  91. */
  92. static void ScaleDownLevel(PLSSUBL plssubl, BOOL* pfAnySublines, BOOL* pfCollectVisual)
  93. {
  94. const PLSC plsc = plssubl->plsc;
  95. LSTFLOW lstflow = plssubl->lstflow;
  96. const DWORD iobjText = IobjTextFromLsc(&(plsc->lsiobjcontext));
  97. LSDEVRES* pdevres = &(plsc->lsdocinf.lsdevres);
  98. PLSDNODE pdn = plssubl->plsdnFirst;
  99. DWORD i;
  100. BOOL fDummy;
  101. BOOL fSeeReasonForVisualLine = fFalse;
  102. while (pdn != NULL) /* don't care about break */
  103. {
  104. if (FIsDnodeReal(pdn))
  105. {
  106. if (pdn->u.real.lschp.fUnderline || pdn->u.real.lschp.fShade || pdn->u.real.lschp.fBorder)
  107. {
  108. fSeeReasonForVisualLine = fTrue;
  109. }
  110. if (IdObjFromDnode(pdn) == iobjText)
  111. {
  112. if (pdn->fTab)
  113. pdn->u.real.dup = UpFromUr(lstflow, pdevres, pdn->u.real.objdim.dur);
  114. }
  115. else
  116. {
  117. if (!pdn->fRigidDup)
  118. {
  119. pdn->u.real.dup = UpFromUr(lstflow, pdevres, pdn->u.real.objdim.dur);
  120. }
  121. if (pdn->u.real.pinfosubl != NULL)
  122. {
  123. *pfAnySublines = fTrue;
  124. for (i = 0; i < pdn->u.real.pinfosubl->cSubline; i++)
  125. {
  126. ScaleDownLevel((pdn->u.real.pinfosubl->rgpsubl)[i], &fDummy, pfCollectVisual);
  127. }
  128. }
  129. }
  130. }
  131. else if (FIsDnodePen(pdn))
  132. {
  133. pdn->u.pen.dup = UpFromUr(lstflow, pdevres, pdn->u.pen.dur);
  134. }
  135. else
  136. {
  137. // Borders are rigidDup always - no scaling down
  138. // we'll try to "undo" the moving at display time at the main level if
  139. // fUnderlineTrailSpacesRM is on. So, after prepdisp we want none or only one
  140. // fBorderMovedFromTrailingArea flag remain and the meaning of the flag is:
  141. // I am the border that should be moved back into trailing spaces.
  142. if (pdn->fBorderMovedFromTrailingArea)
  143. {
  144. if (!FIsSubLineMain(pdn->plssubl) ||
  145. !plsc->lsadjustcontext.fUnderlineTrailSpacesRM)
  146. {
  147. pdn->fBorderMovedFromTrailingArea = fFalse;
  148. }
  149. }
  150. }
  151. pdn = pdn->plsdnNext;
  152. }
  153. if (fSeeReasonForVisualLine && !plssubl->fMain)
  154. {
  155. *pfCollectVisual = fTrue;
  156. }
  157. }
  158. // %%Function: FindWallToCollectSublinesAfter
  159. // %%Contact: victork
  160. //
  161. // Finds the last wall - wall after which we will start to use submitted subllines.
  162. // If there are no sublines to participate in justification, pdnLastWall is set to null,
  163. // else it points to the last wall (tab, pen or PLINEBEGIN).
  164. static void FindWallToCollectSublinesAfter(PLSDNODE pdnFirst, LSCP cpLim, BOOL fLineCompressed,
  165. PLSDNODE* ppdnLastWall)
  166. {
  167. PLSDNODE pdn;
  168. BOOL fSublineFound;
  169. // Find last tab.
  170. *ppdnLastWall = PLINEBEGIN;
  171. pdn = pdnFirst;
  172. while (FDnodeBeforeCpLim(pdn, cpLim))
  173. {
  174. if (FIsDnodeReal(pdn))
  175. {
  176. if (pdn->fTab)
  177. {
  178. *ppdnLastWall = pdn;
  179. }
  180. }
  181. else /* pen */
  182. if (!FIsDnodeBorder(pdn) && !pdn->fAdvancedPen) // and not advance pen or border
  183. {
  184. *ppdnLastWall = pdn;
  185. }
  186. pdn = pdn->plsdnNext;
  187. }
  188. // OK, last groupchunk starts with a tab or there is only one groupchunk on the line.
  189. // Are there submitted sublines of our compression/expansion type after it?
  190. fSublineFound = fFalse;
  191. if (*ppdnLastWall == PLINEBEGIN)
  192. {
  193. pdn = pdnFirst;
  194. }
  195. else
  196. {
  197. pdn = (*ppdnLastWall)->plsdnNext;
  198. }
  199. while (FDnodeBeforeCpLim(pdn, cpLim))
  200. {
  201. fSublineFound |= DnodeHasSublineForMe(pdn, fLineCompressed);
  202. pdn = pdn->plsdnNext;
  203. }
  204. if (!fSublineFound)
  205. {
  206. *ppdnLastWall = NULL; // don't need last tab
  207. }
  208. return;
  209. }
  210. // %%Function: CalcPresAutonumbers
  211. // %%Contact: victork
  212. //
  213. /*
  214. * Scales dup for autonumbering dnodes, calls CalcPres for autonumbering object.
  215. *
  216. * We want to have main line start exactly on upStartMainText. To achive that we play with
  217. * the width of "white space" dnode, which today contains a tab (usually) or a space.
  218. * (This dnode is pdnWhiteSpace in the code.) If it is not present, we change width of autonumbering
  219. * object itself. We don't want one of them go negative,so sometimes rounding errors force us
  220. * to move start of the main text to the right.
  221. */
  222. static LSERR CalcPresAutonumbers(PLSLINE plsline, PLSDNODE* pdnStartMainText)
  223. {
  224. LSERR lserr;
  225. const PLSC plsc = plsline->lssubl.plsc;
  226. LSTFLOW lstflow = plsline->lssubl.lstflow;
  227. LSDEVRES* pdevres = &(plsc->lsdocinf.lsdevres);
  228. PLSDNODE pdn, pdnObject, pdnWhiteSpace, pdnToAdjust, pdnAfterAutonumbers;
  229. long dupAdjust, dupClosingBorder = 0;
  230. long dupAutonumbering = 0;
  231. plsline->upStartAutonumberingText = UpFromUr(lstflow, pdevres, plsc->lsadjustcontext.urStartAutonumberingText);
  232. // Find the first dnode after autonumbering sequence
  233. // First find the first dnode with positive cpFirst
  234. pdn = plsline->lssubl.plsdnFirst;
  235. Assert(pdn != NULL && FIsNotInContent(pdn));
  236. pdnAfterAutonumbers = pdn->plsdnNext;
  237. while (pdnAfterAutonumbers != NULL && FIsNotInContent(pdnAfterAutonumbers))
  238. {
  239. pdn = pdnAfterAutonumbers;
  240. pdnAfterAutonumbers = pdn->plsdnNext;
  241. }
  242. // pdnAfterAutonumbers is first dnode in content (with positive cpFirst). It can be NULL.
  243. // But it is not the first dnode after autonumbering sequence if autodecimal tab is present
  244. if (plsc->lsadjustcontext.fAutodecimalTabPresent)
  245. {
  246. Assert(FIsDnodeReal(pdn) && pdn->fTab);
  247. pdnAfterAutonumbers = pdn;
  248. }
  249. // Now go againg through autonumbering sequence
  250. // process opening border
  251. pdn = plsline->lssubl.plsdnFirst;
  252. if (FIsDnodeBorder(pdn))
  253. {
  254. Assert(pdn->cpFirst < 0);
  255. Assert(pdn->fOpenBorder);
  256. pdnObject = pdn->plsdnNext;
  257. dupAutonumbering += pdn->u.pen.dup;
  258. }
  259. else
  260. {
  261. pdnObject = pdn;
  262. }
  263. // process B&N object
  264. Assert(pdnObject != NULL && pdnObject->cpFirst < 0); // B&N object should be there
  265. Assert(FIsDnodeReal(pdnObject));
  266. // scale down dup from dur for the first dnode
  267. pdnObject->u.real.dup = UpFromUr(lstflow, pdevres, pdnObject->u.real.objdim.dur);
  268. dupAutonumbering += pdnObject->u.real.dup;
  269. pdn = pdnObject->plsdnNext;
  270. Assert(pdn != NULL); // line must contain something after B&N dnodes
  271. // process "white space" dnode
  272. if (pdn != pdnAfterAutonumbers && FIsDnodeReal(pdn))
  273. {
  274. pdnWhiteSpace = pdn;
  275. dupAutonumbering += pdnWhiteSpace->u.real.dup;
  276. pdnToAdjust = pdnWhiteSpace;
  277. pdn = pdnWhiteSpace->plsdnNext;
  278. }
  279. else
  280. {
  281. pdnWhiteSpace = NULL;
  282. pdnToAdjust = pdnObject;
  283. }
  284. Assert(pdn != NULL); // line must contain something after B&N dnodes
  285. // process closing border
  286. if (pdn != pdnAfterAutonumbers)
  287. {
  288. Assert(FIsDnodeBorder(pdn));
  289. Assert(!pdn->fOpenBorder);
  290. dupClosingBorder = pdn->u.pen.dup;
  291. dupAutonumbering += dupClosingBorder;
  292. pdn = pdn->plsdnNext;
  293. }
  294. Assert(pdn == pdnAfterAutonumbers);
  295. *pdnStartMainText = pdn;
  296. // change dup of the tab or object dnode to ensure exact main text alignment
  297. dupAdjust = plsline->upStartMainText - plsline->upStartAutonumberingText - dupAutonumbering;
  298. pdnToAdjust->u.real.dup += dupAdjust;
  299. if (pdnToAdjust->u.real.dup < 0)
  300. {
  301. // Rounding errors result in negative dup - better to move starting point of the main line.
  302. // It can lead to the nasty situation of right margin to the left of the line beginning in
  303. // theory, but not in practice. This problem is ignored then.
  304. plsline->upStartMainText -= pdnToAdjust->u.real.dup;
  305. pdnToAdjust->u.real.dup = 0;
  306. }
  307. // do CalcPres for the autonumbering object - it's always lskjNone and not last object on the line
  308. lserr = (*plsc->lsiobjcontext.rgobj[pdnObject->u.real.lschp.idObj].lsim.pfnCalcPresentation)
  309. (pdnObject->u.real.pdobj, pdnObject->u.real.dup, lskjNone, fFalse);
  310. if (lserr != lserrNone)
  311. return lserr;
  312. if (pdnWhiteSpace != NULL)
  313. {
  314. plsline->upLimAutonumberingText = plsline->upStartMainText -
  315. pdnWhiteSpace->u.real.dup - dupClosingBorder;
  316. // If "white space" dnode is not a tab, dup should be set in it.
  317. if (!pdnWhiteSpace->fTab)
  318. {
  319. // It's always lskjNone and not last object on the line for white space dnode
  320. lserr = (*plsc->lsiobjcontext.rgobj[pdnWhiteSpace->u.real.lschp.idObj].lsim.pfnCalcPresentation)
  321. (pdnWhiteSpace->u.real.pdobj, pdnWhiteSpace->u.real.dup, lskjNone, fFalse);
  322. if (lserr != lserrNone)
  323. return lserr;
  324. }
  325. }
  326. else
  327. {
  328. plsline->upLimAutonumberingText = plsline->upStartMainText - dupClosingBorder;
  329. }
  330. return lserrNone;
  331. }
  332. // %%Function: SetJustificationForLastGroupChunk
  333. // %%Contact: victork
  334. //
  335. // Changes lskj and lskalign for the last GC if it should be done
  336. // If not, it's OK to leave these parameters unchanged - so they are kind of I/O
  337. //
  338. // We adjust all group chunks except maybe the last one with lskjNone, so guestion is about
  339. // last GroupChunk only.
  340. //
  341. // We do some tricks with justification mode at the end of line, and the answer depends on
  342. // kind of last tab, end of paragraph, etc.
  343. //
  344. static LSERR SetJustificationForLastGroupChunk(PLSLINE plsline, GrpChnkWall LastWall,
  345. LSKJUST* plskj, LSKALIGN* plskalign)
  346. {
  347. LSERR lserr;
  348. const PLSC plsc = plsline->lssubl.plsc;
  349. LSKJUST lskjPara = plsc->lsadjustcontext.lskj;
  350. LSKALIGN lskalignPara = plsc->lsadjustcontext.lskalign;
  351. ENDRES endr = plsline->lslinfo.endr;
  352. BOOL fJustify;
  353. // no justification intended - lskj remains None, lskalign unchanged
  354. if ((lskjPara == lskjNone || lskjPara == lskjSnapGrid) && lskalignPara == lskalLeft)
  355. {
  356. return lserrNone;
  357. }
  358. // Line ends in a normal way - we apply justification, lskalign unchanged
  359. if (endr == endrNormal || endr == endrHyphenated)
  360. {
  361. *plskj = lskjPara;
  362. return lserrNone;
  363. }
  364. // break-through tab kills justification, alignment games
  365. if (FBreakthroughLine(plsc))
  366. {
  367. return lserrNone;
  368. }
  369. // if last Left Wall is non-left tab, justification is off too
  370. if (LastWall.kwall == Tab && LastWall.lsktab != lsktLeft)
  371. {
  372. // we used to return here
  373. // Now we want to give Word a chance to change lskalign from Right to Left for
  374. // the last line in paragraph after text box.
  375. // REVIEW (Victork) Should we call pfnFGetLastLineJustification always?
  376. lskjPara = lskjNone;
  377. }
  378. // What's the matter behind the callback.
  379. //
  380. // They say: no full justification for the last line in paragraph. What does this exactly mean?
  381. // For example, Latin and FE word make different decisions for endrEndSection line.
  382. // Let's ask.
  383. //
  384. // Additional parameter is added to cover the behavior full-justified text wrapping a textbox (bug 682)
  385. // A lone word should be aligned to the right to create a full-justified page, but not at the end of
  386. // the paragraph.
  387. lserr = (*plsc->lscbk.pfnFGetLastLineJustification)(plsc->pols, lskjPara, lskalignPara, endr,
  388. &fJustify, plskalign);
  389. if (lserr != lserrNone) return lserr;
  390. if (fJustify)
  391. {
  392. *plskj = lskjPara;
  393. }
  394. return lserrNone;
  395. }
  396. // %%Function: GetDistanceToTabPoint
  397. // %%Contact: victork
  398. //
  399. /*
  400. * Calculate DistanceToTabPoint given GrpChnk and first Dnode
  401. *
  402. * TabPoint is decimal point for the decimal tab, wchCharTab for character tab
  403. */
  404. static LSERR GetDistanceToTabPoint(GRCHUNKEXT* pgrchunkext, LSCP cpLim, LSKTAB lsktab, WCHAR wchCharTab,
  405. PLSDNODE pdnFirst, long* pdupToTabPoint)
  406. {
  407. LSERR lserr;
  408. DWORD igrchnk; /* # of dnodes before dnode with the point */
  409. long dupToPointInsideDnode;
  410. PLSDNODE pdnTabPoint;
  411. if (pgrchunkext->durTotal == 0)
  412. {
  413. *pdupToTabPoint = 0;
  414. return lserrNone;
  415. }
  416. lserr = CollectTextGroupChunk(pdnFirst, cpLim, CollectSublinesForDecimalTab, pgrchunkext);
  417. if (lserr != lserrNone)
  418. return lserr;
  419. if (lsktab == lsktDecimal)
  420. {
  421. lserr = LsGetDecimalPoint(&(pgrchunkext->lsgrchnk), lsdevPres, &igrchnk, &dupToPointInsideDnode);
  422. }
  423. else
  424. {
  425. Assert(lsktab == lsktChar);
  426. lserr = LsGetCharTab(&(pgrchunkext->lsgrchnk), wchCharTab, lsdevPres, &igrchnk, &dupToPointInsideDnode);
  427. }
  428. if (lserr != lserrNone)
  429. return lserr;
  430. if (igrchnk == ichnkOutside) // no TabPoint in the whole grpchnk
  431. {
  432. // we say: pretend it is right after last dnode (in logical sequence)
  433. pdnTabPoint = pgrchunkext->plsdnLastUsed;
  434. dupToPointInsideDnode = DupFromDnode(pdnTabPoint);
  435. }
  436. else
  437. {
  438. pdnTabPoint = pgrchunkext->plschunkcontext->pplsdnChunk[igrchnk];
  439. }
  440. // We now have the distance between TabPoint and the beginning of the dnode containing it.
  441. // FindPointOffset will add the dup's of all dnodes before that dnode.
  442. FindPointOffset(pdnFirst, lsdevPres, LstflowFromDnode(pdnFirst), CollectSublinesForDecimalTab,
  443. pdnTabPoint, dupToPointInsideDnode, pdupToTabPoint);
  444. return lserrNone;
  445. }
  446. // %%Function: WidenNonTextObjects
  447. // %%Contact: victork
  448. //
  449. /*
  450. * Add dupToAddToNonTextObjects to the width of first cNonTextObjectsToExtend in the GroupChunk
  451. */
  452. static void WidenNonTextObjects(GRCHUNKEXT* pgrchunkext, long dupToAdd, DWORD cObjects)
  453. {
  454. PLSDNODE pdn;
  455. long dupAddToEveryone;
  456. long dupDistributeToFew;
  457. long dupAddToThis;
  458. long dupCurrentSum;
  459. DWORD cObjectsLeft, i;
  460. Assert(cObjects != 0);
  461. Assert(dupToAdd > 0);
  462. dupAddToEveryone = dupToAdd / cObjects;
  463. dupDistributeToFew = dupToAdd - (dupAddToEveryone * cObjects);
  464. cObjectsLeft = cObjects;
  465. dupCurrentSum = 0;
  466. /*
  467. * Following loop tries to distribute remaining dupDistributeToFew pixels evenly.
  468. *
  469. * Algorithm would be easy if fractions are allowed and you can see it in comments;
  470. * The actual algorithm avoids fractions by multiplying everything by cObjects
  471. */
  472. i = 0;
  473. while (cObjectsLeft > 0)
  474. {
  475. Assert(i < pgrchunkext->cNonTextObjects);
  476. pdn = (pgrchunkext->pplsdnNonText)[i];
  477. Assert(pdn != NULL && FIsDnodeReal(pdn) /* && IdObjFromDnode(pdn) != iobjText */ );
  478. if ((pgrchunkext->pfNonTextExpandAfter)[i])
  479. {
  480. dupAddToThis = dupAddToEveryone;
  481. dupCurrentSum += dupDistributeToFew; /* currentSum += Distribute / cObjects; */
  482. if (dupCurrentSum >= (long)cObjects) /* if (currentSum >= 1) */
  483. {
  484. dupAddToThis ++;
  485. dupCurrentSum -= (long)cObjects; /* currentSum--; */
  486. }
  487. pdn->u.real.dup += dupAddToThis;
  488. cObjectsLeft --;
  489. }
  490. i++;
  491. }
  492. return;
  493. }
  494. // %%Function: ConvertAutoTabToPen
  495. // %%Contact: victork
  496. //
  497. static void ConvertAutoTabToPen(PLSLINE plsline, PLSDNODE pdnAutoDecimalTab)
  498. {
  499. long dup, dur;
  500. Assert(pdnAutoDecimalTab->fTab); /* it's still a tab */
  501. dup = pdnAutoDecimalTab->u.real.dup;
  502. dur = pdnAutoDecimalTab->u.real.objdim.dur;
  503. pdnAutoDecimalTab->klsdn = klsdnPenBorder;
  504. pdnAutoDecimalTab->fAdvancedPen = fFalse;
  505. pdnAutoDecimalTab->fTab = fFalse;
  506. pdnAutoDecimalTab->icaltbd = 0;
  507. pdnAutoDecimalTab->u.pen.dup = dup;
  508. pdnAutoDecimalTab->u.pen.dur = dur;
  509. pdnAutoDecimalTab->u.pen.dvp = 0;
  510. pdnAutoDecimalTab->u.pen.dvr = 0;
  511. plsline->fNonRealDnodeEncounted = fTrue;
  512. }
  513. // %%Function: CalcPresForDnodeWithSublines
  514. // %%Contact: victork
  515. //
  516. static LSERR CalcPresForDnodeWithSublines(PLSC plsc, PLSDNODE pdn, BOOL fLineCompressed,
  517. LSKJUST lskj, BOOL fLastOnLine)
  518. {
  519. PLSSUBL* rgpsubl;
  520. DWORD i;
  521. LSTFLOW lstflow; // dummy parameter
  522. LSERR lserr;
  523. long dupSubline;
  524. long dupDnode = 0;
  525. COLLECTSUBLINES CollectGroupChunkPurpose;
  526. Assert(DnodeHasSublineForMe(pdn, fLineCompressed));
  527. // calculate dup for dnode with sublines that took part in justification
  528. if (fLineCompressed)
  529. {
  530. CollectGroupChunkPurpose = CollectSublinesForCompression;
  531. }
  532. else
  533. {
  534. CollectGroupChunkPurpose = CollectSublinesForJustification;
  535. }
  536. rgpsubl = pdn->u.real.pinfosubl->rgpsubl;
  537. for (i = 0; i < pdn->u.real.pinfosubl->cSubline; i++)
  538. {
  539. // fLastOnLine is always false on lower levels
  540. lserr = CalcPresChunk(plsc, rgpsubl[i]->plsdnFirst, rgpsubl[i]->plsdnLastDisplay,
  541. CollectGroupChunkPurpose, fLineCompressed, lskj, fFalse);
  542. if (lserr != lserrNone)
  543. return lserr;
  544. LssbGetDupSubline(rgpsubl[i], &lstflow, &dupSubline);
  545. dupDnode += dupSubline;
  546. (rgpsubl[i])->fDupInvalid = fFalse;
  547. }
  548. // fill dup and call CalcPresentation
  549. pdn->u.real.dup = dupDnode;
  550. lserr = (*plsc->lsiobjcontext.rgobj[pdn->u.real.lschp.idObj].lsim.pfnCalcPresentation)
  551. (pdn->u.real.pdobj, dupDnode, lskj, fLastOnLine);
  552. if (lserr != lserrNone)
  553. return lserr;
  554. return lserrNone;
  555. }
  556. // %%Function: CalcPresChunk
  557. // %%Contact: victork
  558. //
  559. /*
  560. * Calls CalcPresentation for all non-text objects on the chunk.
  561. * That means 1) all dnodes in all GroupChunks (including dnodes in submitted sublines)
  562. * 2) all dnodes that have submitted sublines
  563. *
  564. * Foreign object on the upper level, which is followed only by trailing spaces,
  565. * should be called with fLastOnLine == fTrue.
  566. * Input boolean says whether the input groupchunk is the last on line.
  567. *
  568. * Sets dup for justified sublines
  569. */
  570. static LSERR CalcPresChunk(PLSC plsc, PLSDNODE pdnFirst, PLSDNODE pdnLast,
  571. COLLECTSUBLINES CollectGroupChunkPurpose, BOOL fLineCompressed,
  572. LSKJUST lskj, BOOL fLastOnLine)
  573. {
  574. LSERR lserr;
  575. const DWORD iobjText = IobjTextFromLsc(&(plsc->lsiobjcontext));
  576. BOOL fCollecting;
  577. PLSDNODE pdn;
  578. long dupTailDnode; // dummy parameter - will not use
  579. DWORD cNumOfTrailers;
  580. fCollecting = (CollectGroupChunkPurpose != CollectSublinesNone);
  581. Assert(pdnFirst != NULL);
  582. Assert(pdnLast != NULL);
  583. pdn = pdnLast;
  584. // go backwards to switch fLastOnLine off once we are not in trailing spaces
  585. for (;;)
  586. {
  587. if (FIsDnodeReal(pdn))
  588. if (IdObjFromDnode(pdn) == iobjText)
  589. {
  590. if (fLastOnLine == fTrue)
  591. {
  592. GetTrailInfoText(pdn->u.real.pdobj, pdn->dcp, &cNumOfTrailers, &dupTailDnode);
  593. if (cNumOfTrailers < pdn->dcp)
  594. {
  595. fLastOnLine = fFalse; // trailing spaces stop here
  596. }
  597. }
  598. }
  599. else
  600. {
  601. if (fCollecting && DnodeHasSublineForMe(pdn, fLineCompressed))
  602. {
  603. lserr = CalcPresForDnodeWithSublines(plsc, pdn, fLineCompressed, lskj, fLastOnLine);
  604. if (lserr != lserrNone)
  605. return lserr;
  606. }
  607. else
  608. {
  609. lserr = (*plsc->lsiobjcontext.rgobj[pdn->u.real.lschp.idObj].lsim.pfnCalcPresentation)
  610. (pdn->u.real.pdobj, pdn->u.real.dup, lskj, fLastOnLine);
  611. if (lserr != lserrNone)
  612. return lserr;
  613. }
  614. fLastOnLine = fFalse;
  615. }
  616. if (pdn == pdnFirst)
  617. {
  618. break;
  619. }
  620. pdn = pdn->plsdnPrev;
  621. Assert(pdn != NULL); // we'll encounter pdnFirst first
  622. }
  623. return lserrNone;
  624. }
  625. // %%Function: UpdateUpLimUnderline
  626. // %%Contact: victork
  627. //
  628. /*
  629. * Change upLimUnderline to underline trailing spaces, but not EOP.
  630. * Notice that from now on upLimUnderline doesn't equals upStartTrailing anymore
  631. */
  632. static void UpdateUpLimUnderline(PLSLINE plsline, long dupTail)
  633. {
  634. PLSDNODE pdnLast;
  635. plsline->upLimUnderline += dupTail;
  636. // Now EOPs - they are alone in the last dnode or have some borders around them
  637. if (plsline->lslinfo.endr == endrEndPara ||
  638. plsline->lslinfo.endr == endrAltEndPara ||
  639. plsline->lslinfo.endr == endrEndParaSection ||
  640. plsline->lslinfo.endr == endrSoftCR)
  641. {
  642. pdnLast = plsline->lssubl.plsdnLastDisplay;
  643. Assert(FIsDnodeReal(pdnLast)); // no borders in trailing spaces area
  644. Assert(pdnLast->dcp == 1);
  645. Assert(pdnLast->u.real.dup <= dupTail);
  646. plsline->upLimUnderline -= pdnLast->u.real.dup;
  647. pdnLast = pdnLast->plsdnPrev;
  648. }
  649. // This option extends underlining only up to the right margin
  650. if (plsline->upLimUnderline > plsline->upRightMarginJustify)
  651. {
  652. plsline->upLimUnderline = plsline->upRightMarginJustify;
  653. }
  654. }
  655. // %%Function: PrepareLineForDisplayProc
  656. // %%Contact: victork
  657. //
  658. /*
  659. * PrepareLineForDisplayProc fills in the dup's in dnode list and lsline
  660. *
  661. * Input dnode list consists of "normal dnode list" of dnodes with positive non-negative cp,
  662. * which can be preceded (in this order) by B&N sequence either and/or one Autotab dnode.
  663. *
  664. * B&N sequence is OpeningBorder+AutonumberingObject+TabOrSpace+ClosingBorder.
  665. * ClosingBorder or both OpeningBorder and ClosingBorder can be missing. TabOrSpace can be
  666. * missing too. B&N sequence starts at urStartAutonumberingText and ends at urStartMainText.
  667. * Tab in B&N sequence should not be resolved in a usual way.
  668. *
  669. * Autotab dnode has negative cpFirst, but starts at urStartMainText. It is to be resolved in
  670. * a usual way and then be replaced by a pen dnode.
  671. */
  672. LSERR PrepareLineForDisplayProc(PLSLINE plsline)
  673. {
  674. LSERR lserr;
  675. const PLSC plsc = plsline->lssubl.plsc;
  676. const DWORD iobjText = IobjTextFromLsc(&(plsc->lsiobjcontext));
  677. LSDEVRES* pdevres = &(plsc->lsdocinf.lsdevres);
  678. LSTFLOW lstflow = plsline->lssubl.lstflow; /* text flow of the line */
  679. BOOL fVertical = lstflow & fUVertical;
  680. long dupText, dupTail, dupTailDnode;
  681. DWORD cNumOfTrailers;
  682. PLSDNODE pdn;
  683. BOOL fLastOnLine;
  684. DWORD i;
  685. PDOBJ pdobj[txtobjMaxM]; // quick group chunk
  686. Assert(FIsLSLINE(plsline));
  687. // Next assert means that client should destroy line immediately after creating it
  688. // if fDisplay is set to fFalse in LsSetDoc.
  689. Assert(FDisplay(plsc));
  690. if (!plsline->lssubl.fDupInvalid) /* line has been prepared earlier */
  691. return lserrNone;
  692. Assert(plsc->lsstate == LsStateFree);
  693. Assert(plsc->plslineCur == plsline);
  694. plsc->lsstate = LsStatePreparingForDisplay;
  695. // first try to recognize quick cases, call slow PredDisp otherwise
  696. if (plsc->lsadjustcontext.lskj != lskjNone ||
  697. plsc->lsadjustcontext.lskalign != lskalLeft ||
  698. plsc->lsadjustcontext.lsbrj != lsbrjBreakJustify ||
  699. plsc->lsadjustcontext.fNominalToIdealEncounted ||
  700. plsc->lsadjustcontext.fSubmittedSublineEncounted ||
  701. plsline->fNonRealDnodeEncounted ||
  702. plsline->lssubl.plsdnFirst == NULL ||
  703. FIsNotInContent(plsline->lssubl.plsdnFirst))
  704. {
  705. return PrepareLineForDisplay(plsline);
  706. }
  707. if (plsc->lsdocinf.fPresEqualRef && !FSuspectDeviceDifferent(PlnobjFromLsline(plsline, iobjText)))
  708. {
  709. // Trident quick case - no need to scale down. Dups are already set in text dnodes.
  710. // go through dnode list to calculate dupTrail and CalcPres foreign objects
  711. pdn = plsline->lssubl.plsdnLastDisplay;
  712. dupTail = 0;
  713. fLastOnLine = fTrue;
  714. while (pdn != NULL && IdObjFromDnode(pdn) == iobjText)
  715. {
  716. Assert(pdn->u.real.dup == pdn->u.real.objdim.dur);
  717. GetTrailInfoText(pdn->u.real.pdobj, pdn->dcp, &cNumOfTrailers, &dupTailDnode);
  718. dupTail += dupTailDnode;
  719. if (cNumOfTrailers < pdn->dcp)
  720. {
  721. fLastOnLine = fFalse; // trailing spaces stop here
  722. break; // text is the last on the line
  723. }
  724. pdn = pdn->plsdnPrev;
  725. }
  726. // dupTail is calculated, we still should call pfnCalcPresentation for foreing objects
  727. if (plsc->lsadjustcontext.fForeignObjectEncounted)
  728. {
  729. while (pdn != NULL)
  730. {
  731. Assert(pdn->u.real.dup == pdn->u.real.objdim.dur);
  732. if (IdObjFromDnode(pdn) != iobjText)
  733. {
  734. lserr = (*plsc->lsiobjcontext.rgobj[pdn->u.real.lschp.idObj].lsim.pfnCalcPresentation)
  735. (pdn->u.real.pdobj, pdn->u.real.dup, lskjNone, fLastOnLine);
  736. if (lserr != lserrNone)
  737. {
  738. plsc->lsstate = LsStateFree;
  739. return lserr;
  740. }
  741. fLastOnLine = fFalse; // only the first coulsd be the last on the line
  742. }
  743. pdn = pdn->plsdnPrev;
  744. }
  745. }
  746. plsline->lssubl.fDupInvalid = fFalse;
  747. plsline->upRightMarginJustify = plsc->lsadjustcontext.urRightMarginJustify;
  748. plsline->upStartMainText = plsc->lsadjustcontext.urStartMainText;
  749. plsline->upStartAutonumberingText = plsline->upStartMainText;
  750. plsline->upLimAutonumberingText = plsline->upStartMainText;
  751. plsline->upLimLine = plsline->lssubl.urCur;
  752. plsline->upStartTrailing = plsline->upLimLine - dupTail;
  753. plsline->upLimUnderline = plsline->upStartTrailing;
  754. plsline->fCollectVisual = fFalse;
  755. if (plsc->lsadjustcontext.fUnderlineTrailSpacesRM &&
  756. plsline->upLimUnderline < plsline->upRightMarginJustify)
  757. {
  758. UpdateUpLimUnderline(plsline, dupTail);
  759. }
  760. plsc->lsstate = LsStateFree;
  761. return lserrNone;
  762. }
  763. if ((plsc->grpfManager & fFmiPresExactSync) != 0 &&
  764. !plsc->lsadjustcontext.fForeignObjectEncounted &&
  765. !plsc->lsadjustcontext.fNonLeftTabEncounted &&
  766. plsline->lssubl.plsdnLastDisplay != NULL && // empty line is not a quick case ;(
  767. FQuickScaling(PlnobjFromLsline(plsline, iobjText), fVertical,
  768. plsline->lssubl.urCur - plsc->lsadjustcontext.urStartMainText))
  769. {
  770. // Looks like Word quick case
  771. // We can still go slow way if all trailing spaces are not in one dnode
  772. if (plsline->lslinfo.endr == endrEndPara)
  773. {
  774. Assert(FIsDnodeReal(plsline->lssubl.plsdnLastDisplay));
  775. Assert(plsline->lssubl.plsdnLastDisplay->dcp == 1);
  776. pdn = plsline->lssubl.plsdnLastDisplay->plsdnPrev;
  777. if (pdn != NULL)
  778. {
  779. GetTrailInfoText(pdn->u.real.pdobj, pdn->dcp, &cNumOfTrailers, &dupTailDnode);
  780. if (cNumOfTrailers > 0)
  781. {
  782. // There are spaces before EOP - go slow way
  783. return PrepareLineForDisplay(plsline);
  784. }
  785. }
  786. cNumOfTrailers = 1;
  787. }
  788. else
  789. {
  790. pdn = plsline->lssubl.plsdnLastDisplay;
  791. GetTrailInfoText(pdn->u.real.pdobj, pdn->dcp, &cNumOfTrailers, &dupTailDnode);
  792. if (cNumOfTrailers == pdn->dcp)
  793. {
  794. // We can't be sure all spaces are in this dnode - forget it then
  795. return PrepareLineForDisplay(plsline);
  796. }
  797. }
  798. // we are sure now that all cNumOfTrailers trailing spaces are in the last dnode
  799. // fill standard output part, upStartMainText will be used below
  800. plsline->lssubl.fDupInvalid = fFalse;
  801. plsline->fCollectVisual = fFalse;
  802. plsline->upRightMarginJustify = UpFromUr(lstflow, pdevres, plsc->lsadjustcontext.urRightMarginJustify);
  803. plsline->upStartMainText = UpFromUr(lstflow, pdevres, plsc->lsadjustcontext.urStartMainText);
  804. plsline->upStartAutonumberingText = plsline->upStartMainText;
  805. plsline->upLimAutonumberingText = plsline->upStartMainText;
  806. if (!plsc->lsadjustcontext.fTabEncounted)
  807. {
  808. // Very nice, we have only one groupchunk to collect
  809. for (pdn = plsline->lssubl.plsdnFirst, i = 0;;)
  810. {
  811. Assert(FIsDnodeReal(pdn));
  812. Assert(IdObjFromDnode(pdn) == iobjText);
  813. // i never gets outside of pdobj array.
  814. // Text makes sure in FQuickscaling
  815. Assert(i < txtobjMaxM);
  816. pdobj[i] = pdn->u.real.pdobj;
  817. i++;
  818. if (pdn == plsline->lssubl.plsdnLastDisplay)
  819. {
  820. break;
  821. }
  822. pdn = pdn->plsdnNext;
  823. }
  824. QuickAdjustExact(&(pdobj[0]), i, cNumOfTrailers, fVertical, &dupText, &dupTail);
  825. plsline->upRightMarginJustify = UpFromUr(lstflow, pdevres, plsc->lsadjustcontext.urRightMarginJustify);
  826. plsline->upStartMainText = UpFromUr(lstflow, pdevres, plsc->lsadjustcontext.urStartMainText);
  827. plsline->upStartAutonumberingText = plsline->upStartMainText;
  828. plsline->upLimAutonumberingText = plsline->upStartMainText;
  829. plsline->upLimLine = plsline->upStartMainText + dupText;
  830. plsline->upStartTrailing = plsline->upLimLine - dupTail;
  831. plsline->upLimUnderline = plsline->upStartTrailing;
  832. plsline->fCollectVisual = fFalse;
  833. if (plsc->lsadjustcontext.fUnderlineTrailSpacesRM &&
  834. plsline->upLimUnderline < plsline->upRightMarginJustify)
  835. {
  836. UpdateUpLimUnderline(plsline, dupTail);
  837. }
  838. plsc->lsstate = LsStateFree;
  839. return lserrNone;
  840. }
  841. else
  842. {
  843. // Tabs are present, but they all are left tabs
  844. pdn = plsline->lssubl.plsdnFirst;
  845. plsline->upLimLine = plsline->upStartMainText;
  846. // Do one QuickGroupChunk after another, moving plsline->upLimLine
  847. for (;;)
  848. {
  849. // loop body: Collect next QuickGroupChunk, deal with it, exit after last one
  850. for (i = 0;;)
  851. {
  852. Assert(FIsDnodeReal(pdn));
  853. Assert(IdObjFromDnode(pdn) == iobjText);
  854. if (pdn->fTab)
  855. {
  856. break;
  857. }
  858. Assert(i < txtobjMaxM);
  859. pdobj[i] = pdn->u.real.pdobj;
  860. i++;
  861. if (pdn == plsline->lssubl.plsdnLastDisplay)
  862. {
  863. break;
  864. }
  865. pdn = pdn->plsdnNext;
  866. }
  867. Assert(pdn == plsline->lssubl.plsdnLastDisplay || pdn->fTab);
  868. if (pdn->fTab)
  869. {
  870. long upTabStop;
  871. if (i == 0)
  872. {
  873. dupText = 0;
  874. dupTail = 0;
  875. }
  876. else
  877. {
  878. QuickAdjustExact(pdobj, i, 0, fVertical, &dupText, &dupTail);
  879. }
  880. Assert(plsc->lstabscontext.pcaltbd[pdn->icaltbd].lskt == lsktLeft);
  881. upTabStop = UpFromUr(lstflow, pdevres, plsc->lstabscontext.pcaltbd[pdn->icaltbd].ur);
  882. pdn->u.real.dup = upTabStop - plsline->upLimLine - dupText;
  883. plsline->upLimLine = upTabStop;
  884. if (pdn == plsline->lssubl.plsdnLastDisplay)
  885. {
  886. break;
  887. }
  888. pdn = pdn->plsdnNext;
  889. }
  890. else
  891. {
  892. Assert(i != 0);
  893. QuickAdjustExact(pdobj, i, cNumOfTrailers, fVertical, &dupText, &dupTail);
  894. plsline->upLimLine += dupText;
  895. break;
  896. }
  897. }
  898. plsline->upStartTrailing = plsline->upLimLine - dupTail;
  899. plsline->upLimUnderline = plsline->upStartTrailing;
  900. if (plsc->lsadjustcontext.fUnderlineTrailSpacesRM &&
  901. plsline->upLimUnderline < plsline->upRightMarginJustify)
  902. {
  903. UpdateUpLimUnderline(plsline, dupTail);
  904. }
  905. plsc->lsstate = LsStateFree;
  906. return lserrNone;
  907. }
  908. }
  909. // Getting here means quick prepdisp haven't happen
  910. return PrepareLineForDisplay(plsline);
  911. }
  912. /*
  913. * This is slow and painstaking procedure that does everyting.
  914. * Called when QuickPrep above cannot cope.
  915. */
  916. static LSERR PrepareLineForDisplay(PLSLINE plsline)
  917. {
  918. LSERR lserr = lserrNone;
  919. const PLSC plsc = plsline->lssubl.plsc;
  920. LSTFLOW lstflow = plsline->lssubl.lstflow; /* text flow of the subline */
  921. const DWORD iobjText = IobjTextFromLsc(&(plsc->lsiobjcontext));
  922. LSDEVRES* pdevres = &(plsc->lsdocinf.lsdevres);
  923. long urColumnMax = plsc->lsadjustcontext.urRightMarginJustify;
  924. long upColumnMax = UpFromUr(lstflow, pdevres, urColumnMax);
  925. LSCP cpLim = plsline->lssubl.cpLimDisplay;
  926. PLSDNODE pdnFirst = plsline->lssubl.plsdnFirst; /* the first dnode of the line */
  927. PLSDNODE pdnAutoDecimalTab = NULL; /* NULL means - no such a thing on the line */
  928. GRCHUNKEXT grchunkext;
  929. BOOL fEmptyGroupChunk;
  930. PLSDNODE pdnLastWall = NULL; /* last wall with submitted sublines after it */
  931. BOOL fAnySublines = fFalse;
  932. COLLECTSUBLINES CollectGroupChunkPurpose = (plsc->lsadjustcontext.fLineCompressed) ?
  933. CollectSublinesForCompression : CollectSublinesForJustification;
  934. // parameters to call AdjustText
  935. LSKJUST lskj = lskjNone; /* These four will be changed only when calling */
  936. BOOL fForcedBreak = fFalse; /* AdjustText last time on the line */
  937. BOOL fSuppressTrailingSpaces = fFalse; /* if ever */
  938. LSKALIGN lskalign = plsc->lsadjustcontext.lskalign; // Alignment can be changed too
  939. long dupAvailable;
  940. BOOL fExact;
  941. BOOL fSuppressWiggle;
  942. long dupText, dupTail = 0, dupToAddToNonTextObjects;
  943. long durColumnMax;
  944. DWORD cNonTextObjectsToExtend;
  945. PLSDNODE pdnNextFirst; /* first Dnode of the next GrpChnk */
  946. GrpChnkWall LeftWall, RightWall; /* current TextGroupChunk walls */
  947. long upLeftWall,urLeftWall; /* Left wall position */
  948. long dupWall, durWall;
  949. long dupGrpChnk;
  950. long dupToTabPoint;
  951. long dupJustifyLine;
  952. LSKTAB lsktabLast = lsktLeft;
  953. long dupLastTab = 0;
  954. long upLeftWallForCentering;
  955. PLSDNODE pdnLast;
  956. InitGroupChunkExt(plsline->lssubl.plschunkcontext, iobjText, &grchunkext); /* prepare one GRCHUNKEXT for all */
  957. plsline->upStartMainText = UpFromUr(lstflow, pdevres, plsc->lsadjustcontext.urStartMainText);
  958. // set defaults incase there are no autonumbering
  959. plsline->upStartAutonumberingText = plsline->upStartMainText;
  960. plsline->upLimAutonumberingText = plsline->upStartMainText;
  961. // fCollectVisual can be reset to fTrue by ScaleDownLevel called here or in AdjustSubline
  962. plsline->fCollectVisual = fFalse;
  963. if (!plsline->fAllSimpleText)
  964. {
  965. /* straighforward scaling down of non-text objects */
  966. ScaleDownLevel(&(plsline->lssubl), &fAnySublines, &(plsline->fCollectVisual));
  967. if (plsc->lsadjustcontext.fLineContainsAutoNumber)
  968. {
  969. // do dup setting for autonumbers, update pdnFirst to point after it
  970. lserr = CalcPresAutonumbers(plsline, &pdnFirst);
  971. if (lserr != lserrNone)
  972. {
  973. plsc->lsstate = LsStateFree;
  974. return lserr;
  975. }
  976. }
  977. // If autodecimal tab is there, pdnFirst points at it - make a note.
  978. // This tab can only be just before main text and it has negative cpFirst
  979. // Check for NULL is needed because empty dnode list is possible in LS.
  980. // We don't have a dnode for splat, so we'll get here with pdnFirst == NULL
  981. // when line is (object that said delete me) + splat
  982. if (plsc->lsadjustcontext.fAutodecimalTabPresent)
  983. {
  984. Assert(pdnFirst != NULL && FIsNotInContent(pdnFirst) && pdnFirst->fTab);
  985. // It doesn't need any special handling even having negative cpFirst
  986. // We note it to convert to pen later
  987. pdnAutoDecimalTab = pdnFirst;
  988. }
  989. if (fAnySublines)
  990. {
  991. // Find last tab and prepare sublines after it.
  992. FindWallToCollectSublinesAfter(pdnFirst, cpLim, plsc->lsadjustcontext.fLineCompressed,
  993. &pdnLastWall);
  994. }
  995. }
  996. /*
  997. * Loop structure : While !end_of_line do
  998. * {
  999. * get next Wall (collect GrpChnk);
  1000. * adjust GrpChnk;
  1001. * set dup of the tab to the left of the GrpChnk;
  1002. * move one Wall to the right
  1003. * }
  1004. *
  1005. * Invariance: all dup before LeftWall are done.
  1006. * upLeftWall is at the beginning of the left wall
  1007. * pdnNextFirst is the dnode to start collecting next GrpChnk with
  1008. */
  1009. pdnNextFirst = pdnFirst;
  1010. LeftWall.kwall = LineBegin;
  1011. LeftWall.pdn = PLINEBEGIN;
  1012. LeftWall.lsktab = lsktLeft; // 4 lines just against asserts
  1013. LeftWall.wchCharTab = 0;
  1014. LeftWall.upTab = 0;
  1015. RightWall = LeftWall;
  1016. upLeftWall = 0;
  1017. urLeftWall = 0;
  1018. while (LeftWall.kwall != LineEnd)
  1019. {
  1020. /* 1. Find next wall (collect GrpChnk or skip collecting if two walls in a row)
  1021. *
  1022. * Input: pdnNextFirst - first dnode after Left wall
  1023. *
  1024. * Output: RightWall.pdn & grchunkext.
  1025. * if there is no GrpChnk some zeros in grchunkext is enough
  1026. */
  1027. if (FIsWall(pdnNextFirst, cpLim))
  1028. {
  1029. fEmptyGroupChunk = fTrue;
  1030. RightWall.pdn = pdnNextFirst;
  1031. grchunkext.durTotal = 0;
  1032. grchunkext.durTextTotal = 0;
  1033. grchunkext.dupNonTextTotal = 0;
  1034. }
  1035. else
  1036. {
  1037. lserr = CollectTextGroupChunk(pdnNextFirst, cpLim, CollectGroupChunkPurpose, &grchunkext);
  1038. if (lserr != lserrNone)
  1039. {
  1040. plsc->lsstate = LsStateFree;
  1041. return lserr;
  1042. }
  1043. if (grchunkext.lsgrchnk.clsgrchnk == 0 && grchunkext.cNonTextObjects == 0)
  1044. {
  1045. // only borders in this groupchunk - no need to call AdjustText
  1046. fEmptyGroupChunk = fTrue;
  1047. grchunkext.durTextTotal = 0;
  1048. }
  1049. else
  1050. {
  1051. fEmptyGroupChunk = fFalse;
  1052. }
  1053. RightWall.pdn = grchunkext.plsdnNext;
  1054. }
  1055. /*
  1056. * 2. fill in Right Wall information
  1057. *
  1058. * Input: RightWall.pdn
  1059. *
  1060. * Output: pdnNextFirst, RightWall information.
  1061. */
  1062. if (!FDnodeBeforeCpLim(RightWall.pdn, cpLim))
  1063. {
  1064. RightWall.kwall = LineEnd;
  1065. }
  1066. else
  1067. {
  1068. Assert(FIsWall(RightWall.pdn, cpLim));
  1069. pdnNextFirst = RightWall.pdn->plsdnNext;
  1070. if (FIsDnodePen(RightWall.pdn))
  1071. {
  1072. RightWall.kwall = Pen;
  1073. }
  1074. else
  1075. {
  1076. Assert(RightWall.pdn->fTab); /* it must be a tab */
  1077. RightWall.kwall = Tab;
  1078. RightWall.lsktab = plsc->lstabscontext.pcaltbd[RightWall.pdn->icaltbd].lskt;
  1079. RightWall.wchCharTab = plsc->lstabscontext.pcaltbd[RightWall.pdn->icaltbd].wchCharTab;
  1080. RightWall.upTab = UpFromUr(lstflow, pdevres, plsc->lstabscontext.pcaltbd[RightWall.pdn->icaltbd].ur);
  1081. }
  1082. }
  1083. /*
  1084. * prepare parameters for AdjustText
  1085. *
  1086. * Input: LeftWall, urLeftWall, upLeftWall, is_it_the_last_one
  1087. *
  1088. * Output: durColumnMax; lskj, dupAvailable and other input parameters for AdjustText
  1089. *
  1090. */
  1091. if (RightWall.kwall != LineEnd)
  1092. {
  1093. if (RightWall.kwall == Tab && RightWall.lsktab == lsktLeft)
  1094. {
  1095. // Now we know for sure what space we have for text in this groupchunk
  1096. // and can do decent job if client doesn't care about fExact
  1097. long upLeft, urLeft, upRight, urRight;
  1098. urRight = plsc->lstabscontext.pcaltbd[RightWall.pdn->icaltbd].ur;
  1099. upRight = UpFromUr(lstflow, pdevres, urRight);
  1100. if (LeftWall.kwall == Tab && LeftWall.lsktab == lsktLeft)
  1101. {
  1102. urLeft = plsc->lstabscontext.pcaltbd[LeftWall.pdn->icaltbd].ur;
  1103. upLeft = UpFromUr(lstflow, pdevres, urLeft);
  1104. }
  1105. else if (LeftWall.kwall == LineBegin)
  1106. {
  1107. urLeft = plsc->lsadjustcontext.urStartMainText;
  1108. upLeft = plsline->upStartMainText;
  1109. }
  1110. else if (LeftWall.kwall == Pen)
  1111. {
  1112. /* pen - it've been scaled already, we know left wall dimensions in advance */
  1113. urLeft = urLeftWall + LeftWall.pdn->u.pen.dur;
  1114. upLeft = upLeftWall + LeftWall.pdn->u.pen.dup;
  1115. }
  1116. else /* now non-left tabs */
  1117. {
  1118. urLeft = urLeftWall;
  1119. upLeft = upLeftWall;
  1120. }
  1121. durColumnMax = urRight - urLeft;
  1122. dupAvailable = upRight - upLeft;
  1123. Assert(durColumnMax >= 0);
  1124. // dupAvailable can be < 0 here - visi optional hyphens in previous GC, for example.
  1125. // AdjustText doesn't mind, meaning it won't crush.
  1126. fSuppressWiggle = ((plsc->grpfManager & fFmiPresSuppressWiggle) != 0);
  1127. fExact = ((plsc->grpfManager & fFmiPresExactSync) != 0);
  1128. }
  1129. else
  1130. {
  1131. // situation is complicated - we go the safest way.
  1132. durColumnMax = grchunkext.durTotal;
  1133. dupAvailable = LONG_MAX;
  1134. fExact = fTrue;
  1135. fSuppressWiggle = fTrue;
  1136. }
  1137. }
  1138. else
  1139. {
  1140. /* for the last GrpChnk we must to calculate durColumnMax and dupAvailable */
  1141. if (LeftWall.kwall == Tab && LeftWall.lsktab == lsktLeft)
  1142. {
  1143. durColumnMax = urColumnMax - plsc->lstabscontext.pcaltbd[LeftWall.pdn->icaltbd].ur;
  1144. dupAvailable = UpFromUr(lstflow, pdevres, urColumnMax) - grchunkext.dupNonTextTotal -
  1145. UpFromUr(lstflow, pdevres, plsc->lstabscontext.pcaltbd[LeftWall.pdn->icaltbd].ur);
  1146. }
  1147. else if (LeftWall.kwall == LineBegin)
  1148. {
  1149. durColumnMax = urColumnMax - plsc->lsadjustcontext.urStartMainText;
  1150. dupAvailable = upColumnMax - plsline->upStartMainText - grchunkext.dupNonTextTotal;
  1151. // Ask AdjustText to set widths of trailing spaces to 0 only if
  1152. // It is the last groupchunk, (we actually only care for "the only one" situation)
  1153. // and its first dnode (again, we actually only care for "the only one" situation)
  1154. // submits subline for both justification and trailing spaces
  1155. // and this subline runs in the direction opposite to the line direction.
  1156. if (!fEmptyGroupChunk &&
  1157. FIsDnodeReal(grchunkext.plsdnFirst) && grchunkext.plsdnFirst->u.real.pinfosubl != NULL &&
  1158. grchunkext.plsdnFirst->u.real.pinfosubl->fUseForJustification &&
  1159. grchunkext.plsdnFirst->u.real.pinfosubl->fUseForTrailingArea &&
  1160. FCollinearTflows(((grchunkext.plsdnFirst->u.real.pinfosubl->rgpsubl)[0])->lstflow, lstflow) &&
  1161. ((grchunkext.plsdnFirst->u.real.pinfosubl->rgpsubl)[0])->lstflow != lstflow)
  1162. {
  1163. fSuppressTrailingSpaces = fTrue;
  1164. }
  1165. }
  1166. else if (LeftWall.kwall == Pen)
  1167. {
  1168. /* pen - it've been scaled already, we know wall dimensions in advance */
  1169. durColumnMax = urColumnMax - urLeftWall - LeftWall.pdn->u.pen.dur;
  1170. dupAvailable = UpFromUr(lstflow, pdevres, urColumnMax) - upLeftWall -
  1171. LeftWall.pdn->u.pen.dup - grchunkext.dupNonTextTotal;
  1172. }
  1173. else /* now non-left tabs */
  1174. {
  1175. durColumnMax = urColumnMax - urLeftWall;
  1176. dupAvailable = UpFromUr(lstflow, pdevres, urColumnMax) - upLeftWall - grchunkext.dupNonTextTotal;
  1177. }
  1178. // we do some tricks with justification mode at the end of line
  1179. // alignment can change too.
  1180. lserr = SetJustificationForLastGroupChunk(plsline, LeftWall, &lskj, &lskalign);
  1181. if (lserr != lserrNone)
  1182. {
  1183. plsc->lsstate = LsStateFree;
  1184. return lserr;
  1185. }
  1186. // Don't try to squeeze into RMJustify is RMBreak is infinite.
  1187. if (plsc->urRightMarginBreak >= uLsInfiniteRM)
  1188. {
  1189. dupAvailable = LONG_MAX;
  1190. }
  1191. fSuppressWiggle = ((plsc->grpfManager & fFmiPresSuppressWiggle) != 0);
  1192. fExact = ((plsc->grpfManager & fFmiPresExactSync) != 0);
  1193. fForcedBreak = plsline->lslinfo.fForcedBreak;
  1194. }
  1195. /*
  1196. * Adjust text (if any)
  1197. *
  1198. * Input: durColumnMax, dupAvailable, lskj and other input parameters
  1199. *
  1200. * Output: dupText and dupTail
  1201. */
  1202. if (fEmptyGroupChunk)
  1203. {
  1204. dupText = 0;
  1205. dupTail = 0;
  1206. dupToAddToNonTextObjects = 0;
  1207. }
  1208. else
  1209. {
  1210. lserr = AdjustText(lskj, durColumnMax, grchunkext.durTotal - grchunkext.durTrailing,
  1211. dupAvailable, &(grchunkext.lsgrchnk),
  1212. &(grchunkext.posichnkBeforeTrailing), lstflow,
  1213. plsc->lsadjustcontext.fLineCompressed && RightWall.kwall == LineEnd,
  1214. grchunkext.cNonTextObjectsExpand,
  1215. fSuppressWiggle, fExact, fForcedBreak, fSuppressTrailingSpaces,
  1216. &dupText, &dupTail, &dupToAddToNonTextObjects, &cNonTextObjectsToExtend);
  1217. if (lserr != lserrNone)
  1218. {
  1219. plsc->lsstate = LsStateFree;
  1220. return lserr;
  1221. }
  1222. // Finish justification by expanding non-text object.
  1223. if (cNonTextObjectsToExtend != 0 && dupToAddToNonTextObjects > 0)
  1224. {
  1225. WidenNonTextObjects(&grchunkext, dupToAddToNonTextObjects, cNonTextObjectsToExtend);
  1226. }
  1227. else
  1228. // We don't compressi and we don't expand the last non-text object on the line
  1229. {
  1230. dupToAddToNonTextObjects = 0; // don't say we did it
  1231. }
  1232. /*
  1233. * Set dup in non-text objects (do CalcPres) in the current GroupChunk
  1234. *
  1235. * The job cannot be postponed until after the end of the main loop and done for the whole line
  1236. * because GetDistanceToDecimalPoint relies on dups in upper level dnodes
  1237. */
  1238. if (!plsline->fAllSimpleText)
  1239. {
  1240. // find the last upper level dnode of the groupchunk
  1241. if (grchunkext.plsdnNext != NULL)
  1242. {
  1243. pdnLast = (grchunkext.plsdnNext)->plsdnPrev;
  1244. }
  1245. else
  1246. {
  1247. Assert(RightWall.kwall == LineEnd);
  1248. pdnLast = plsline->lssubl.plsdnLastDisplay;
  1249. }
  1250. lserr = CalcPresChunk(plsc, grchunkext.plsdnFirst, pdnLast, CollectGroupChunkPurpose,
  1251. plsc->lsadjustcontext.fLineCompressed, lskj, (RightWall.kwall == LineEnd));
  1252. if (lserr != lserrNone)
  1253. {
  1254. plsc->lsstate = LsStateFree;
  1255. return lserr;
  1256. }
  1257. }
  1258. }
  1259. /*
  1260. * Set the left wall (if it's a tab - resolve it)
  1261. *
  1262. * Input: LeftWall, dupText, dupTail, grchunkext.dupNonTextTotal, grchunkext.durTotal,
  1263. * dupToAddToNonTextObjects (grchunkext for decimal tab)
  1264. *
  1265. * Output: dupWall, durWall
  1266. */
  1267. dupGrpChnk = dupText + grchunkext.dupNonTextTotal + dupToAddToNonTextObjects;
  1268. lsktabLast = lsktLeft; // no tab equal left tab for my purpose
  1269. if (LeftWall.kwall == Tab)
  1270. {
  1271. /* calculate dup of the Left wall now */
  1272. if (dupGrpChnk == 0) /* consecutive tabs */
  1273. dupWall = LeftWall.upTab - upLeftWall;
  1274. else
  1275. if (LeftWall.lsktab == lsktLeft)
  1276. dupWall = LeftWall.upTab - upLeftWall;
  1277. else if (LeftWall.lsktab == lsktRight)
  1278. dupWall = LeftWall.upTab - upLeftWall - (dupGrpChnk - dupTail);
  1279. else if (LeftWall.lsktab == lsktCenter)
  1280. dupWall = LeftWall.upTab - upLeftWall - ((dupGrpChnk - dupTail) / 2);
  1281. else /* LeftWall.lsktab == lsktDecimal or lsktChar */
  1282. {
  1283. lserr = GetDistanceToTabPoint(&grchunkext, cpLim, LeftWall.lsktab, LeftWall.wchCharTab,
  1284. LeftWall.pdn->plsdnNext, &dupToTabPoint);
  1285. if (lserr != lserrNone)
  1286. {
  1287. plsc->lsstate = LsStateFree;
  1288. return lserr;
  1289. }
  1290. dupWall = LeftWall.upTab - upLeftWall - dupToTabPoint;
  1291. }
  1292. // take care of previous text and right margin
  1293. if (RightWall.kwall == LineEnd &&
  1294. (upLeftWall + dupWall + dupGrpChnk - dupTail) > upColumnMax)
  1295. {
  1296. // We don't want to cross RM because of last center tab
  1297. dupWall = upColumnMax - upLeftWall - dupGrpChnk + dupTail;
  1298. }
  1299. if (dupWall < 0)
  1300. dupWall = 0;
  1301. /* LeftWall tab resolving */
  1302. LeftWall.pdn->u.real.dup = dupWall;
  1303. durWall = LeftWall.pdn->u.real.objdim.dur;
  1304. // for reproducing Word's bug of forgetting last not-left tab for centering.
  1305. lsktabLast = LeftWall.lsktab;
  1306. dupLastTab = dupWall;
  1307. }
  1308. else if (LeftWall.kwall == Pen)
  1309. {
  1310. dupWall = LeftWall.pdn->u.pen.dup; /* it've been scaled already */
  1311. durWall = LeftWall.pdn->u.pen.dur;
  1312. }
  1313. else /* LeftWall.kwall == LineBegin */
  1314. {
  1315. dupWall = plsline->upStartMainText;
  1316. durWall = plsc->lsadjustcontext.urStartMainText;
  1317. }
  1318. /* update loop variables, move one wall to the right */
  1319. upLeftWall += dupWall + dupGrpChnk;
  1320. urLeftWall += durWall + grchunkext.durTotal;
  1321. LeftWall = RightWall;
  1322. } /* end of the main loop */
  1323. /*
  1324. * prepare output parameters
  1325. */
  1326. plsline->upRightMarginJustify = upColumnMax;
  1327. plsline->upLimLine = upLeftWall;
  1328. plsline->upStartTrailing = upLeftWall - dupTail;
  1329. plsline->upLimUnderline = plsline->upStartTrailing;
  1330. plsline->lssubl.fDupInvalid = fFalse;
  1331. /*
  1332. * Do left margin adjustment (not for breakthrough tab)
  1333. * We're interested in lskalRight and Centered now
  1334. */
  1335. if (lskalign != lskalLeft && !FBreakthroughLine(plsc))
  1336. {
  1337. if (plsc->lsadjustcontext.fForgetLastTabAlignment && lsktabLast != lsktLeft)
  1338. {
  1339. // reproduction of an old Word bug: when last tab was not left and was resolved at line end
  1340. // they forgot to update their counterpart of upLeftWallForCentering. Word still have to be
  1341. // able to show old documents formatted in this crazy way as they were.
  1342. upLeftWallForCentering = upLeftWall - dupLastTab - dupTail;
  1343. }
  1344. else
  1345. {
  1346. upLeftWallForCentering = upLeftWall - dupTail;
  1347. }
  1348. if (lskalign == lskalRight)
  1349. {
  1350. dupJustifyLine = upColumnMax - upLeftWallForCentering;
  1351. }
  1352. else
  1353. {
  1354. /* These logic of centering is too simple to be valid, but Word uses it */
  1355. dupJustifyLine = (upColumnMax - upLeftWallForCentering) / 2;
  1356. }
  1357. // Apply adjustment if hanging punctuation haven't make it negative
  1358. if (dupJustifyLine > 0)
  1359. {
  1360. plsline->upStartAutonumberingText += dupJustifyLine;
  1361. plsline->upLimAutonumberingText += dupJustifyLine;
  1362. plsline->upStartMainText += dupJustifyLine;
  1363. plsline->upLimLine += dupJustifyLine;
  1364. plsline->upStartTrailing += dupJustifyLine;
  1365. plsline->upLimUnderline += dupJustifyLine;
  1366. }
  1367. }
  1368. if (plsc->lsadjustcontext.fUnderlineTrailSpacesRM &&
  1369. plsline->upLimUnderline < plsline->upRightMarginJustify)
  1370. {
  1371. UpdateUpLimUnderline(plsline, dupTail);
  1372. }
  1373. if (pdnAutoDecimalTab != NULL)
  1374. ConvertAutoTabToPen(plsline, pdnAutoDecimalTab);
  1375. plsc->lsstate = LsStateFree;
  1376. return lserr;
  1377. }
  1378. // %%Function: MatchPresSubline
  1379. // %%Contact: victork
  1380. //
  1381. /*
  1382. * Order of operations
  1383. *
  1384. * 1. Straighforward scaling down of non-text objects
  1385. * 2. Adjusting of text by LeftExact
  1386. * 3. Intelligent rescaling of pens to counteract rounding errors and text non-expansion.
  1387. * 4. Calling CalcPresentation for all non-text objects
  1388. */
  1389. LSERR MatchPresSubline(PLSSUBL plssubl)
  1390. {
  1391. LSERR lserr;
  1392. const PLSC plsc = plssubl->plsc;
  1393. LSTFLOW lstflow = plssubl->lstflow; /* text flow of the subline */
  1394. const DWORD iobjText = IobjTextFromLsc(&(plsc->lsiobjcontext));
  1395. LSDEVRES* pdevres = &(plsc->lsdocinf.lsdevres);
  1396. LSCP cpLim = plssubl->cpLimDisplay;
  1397. PLSDNODE pdnFirst = plssubl->plsdnFirst;
  1398. GRCHUNKEXT grchunkext;
  1399. long dupAvailable; /* input for AdjustText */
  1400. long dupText, dupTail, dupToAddToNonTextObjects; /* dummy output for AdjustText */
  1401. DWORD cNonTextObjectsToExtend;
  1402. BOOL fDummy1, fDummy2; // dummy parameters
  1403. long urAlreadyScaled, upAlreadyScaled, upAlreadyScaledNew;
  1404. PLSDNODE pdn;
  1405. Assert(plssubl->fDupInvalid == fTrue);
  1406. /* 1. Straighforward scaling down of non-text objects */
  1407. ScaleDownLevel(plssubl, &fDummy1, &fDummy2);
  1408. /* 2. Adjusting of text on the level by LeftExact */
  1409. InitGroupChunkExt(plssubl->plschunkcontext, iobjText, &grchunkext); /* prepare GRCHUNKEXT */
  1410. pdn = pdnFirst;
  1411. while (pdn != NULL && (pdn->fTab || FIsDnodeNormalPen(pdn))) /* skip GrpChnk Wall(s) */
  1412. pdn = pdn->plsdnNext;
  1413. while (FDnodeBeforeCpLim(pdn, cpLim))
  1414. {
  1415. lserr = CollectTextGroupChunk(pdn, cpLim, CollectSublinesNone, &grchunkext);
  1416. if (lserr != lserrNone)
  1417. return lserr;
  1418. /* Adjust text by Left, Exact, no durFreeSpace, ignore any shortcomings */
  1419. dupAvailable = UpFromUr(lstflow, pdevres, grchunkext.durTotal) - grchunkext.dupNonTextTotal;
  1420. // posichnkBeforeTrailing is undefined when CollectSublinesNone, tell AdjustText about it
  1421. grchunkext.posichnkBeforeTrailing.ichnk = grchunkext.lsgrchnk.clsgrchnk;
  1422. grchunkext.posichnkBeforeTrailing.dcp = 0;
  1423. lserr = AdjustText(lskjNone, grchunkext.durTotal, grchunkext.durTotal,
  1424. dupAvailable,
  1425. &(grchunkext.lsgrchnk),
  1426. &(grchunkext.posichnkBeforeTrailing),
  1427. lstflow,
  1428. fFalse, // compress?
  1429. grchunkext.cNonTextObjects,
  1430. fTrue, // fSuppressWiggle
  1431. fTrue, // fExact
  1432. fFalse, // fForcedBreak
  1433. fFalse, // fSuppressTrailingSpaces
  1434. &dupText, &dupTail, &dupToAddToNonTextObjects, &cNonTextObjectsToExtend);
  1435. if (lserr != lserrNone)
  1436. return lserr;
  1437. pdn = grchunkext.plsdnNext;
  1438. while (pdn != NULL && (pdn->fTab || FIsDnodeNormalPen(pdn))) /* skip GrpChnk Wall(s) */
  1439. pdn = pdn->plsdnNext;
  1440. }
  1441. /* 3-4. Intelligent rescaling of pens to counteract rounding errors and text non-expansion.
  1442. * and adjusting of lower levels. Calling CalcPresentation for non-text objects.
  1443. */
  1444. pdn = pdnFirst;
  1445. urAlreadyScaled = 0;
  1446. upAlreadyScaled = 0;
  1447. while (FDnodeBeforeCpLim(pdn, cpLim))
  1448. {
  1449. if (FIsDnodeReal(pdn))
  1450. {
  1451. urAlreadyScaled += pdn->u.real.objdim.dur;
  1452. upAlreadyScaled += pdn->u.real.dup;
  1453. if (IdObjFromDnode(pdn) != iobjText)
  1454. {
  1455. // It's always lskjNone and not last object on the line for MatchPresSubline
  1456. lserr = (*plsc->lsiobjcontext.rgobj[pdn->u.real.lschp.idObj].lsim.pfnCalcPresentation)
  1457. (pdn->u.real.pdobj, pdn->u.real.dup, lskjNone, fFalse);
  1458. if (lserr != lserrNone)
  1459. return lserr;
  1460. }
  1461. }
  1462. else if (FIsDnodeBorder(pdn))
  1463. {
  1464. // we don't rescale borders to preserve (dupOpeningBorder == dupClosingBorder)
  1465. upAlreadyScaled += pdn->u.real.dup;
  1466. urAlreadyScaled += pdn->u.pen.dur;
  1467. }
  1468. else /* pen */
  1469. {
  1470. urAlreadyScaled += pdn->u.pen.dur;
  1471. upAlreadyScaledNew = UpFromUr(lstflow, pdevres, urAlreadyScaled);
  1472. pdn->u.pen.dup = upAlreadyScaledNew - upAlreadyScaled;
  1473. upAlreadyScaled = upAlreadyScaledNew;
  1474. }
  1475. pdn = pdn->plsdnNext;
  1476. }
  1477. plssubl->fDupInvalid = fFalse;
  1478. return lserrNone;
  1479. }
  1480. // %%Function: AdjustSubline
  1481. // %%Contact: victork
  1482. //
  1483. /*
  1484. *
  1485. * Scale down non-text objects.
  1486. * Collect GroupChunk
  1487. * It should cover the whole subline or else do MatchPresSubline instead.
  1488. * Adjust text by expanding or compressing to given dup.
  1489. * Call CalcPresentation for all non-text objects.
  1490. */
  1491. LSERR AdjustSubline(PLSSUBL plssubl, LSKJUST lskjust, long dup, BOOL fCompress)
  1492. {
  1493. LSERR lserr;
  1494. const PLSC plsc = plssubl->plsc;
  1495. LSTFLOW lstflow = plssubl->lstflow; /* text flow of the subline */
  1496. const DWORD iobjText = IobjTextFromLsc(&(plsc->lsiobjcontext));
  1497. LSDEVRES* pdevres = &(plsc->lsdocinf.lsdevres);
  1498. LSCP cpLim = plssubl->cpLimDisplay;
  1499. PLSDNODE pdnFirst = plssubl->plsdnFirst;
  1500. GRCHUNKEXT grchunkext;
  1501. COLLECTSUBLINES CollectGroupChunkPurpose;
  1502. long dupAvailable, durColumnMax; /* input for AdjustText */
  1503. long dupText, dupTail, dupToAddToNonTextObjects; /* dummy output for AdjustText */
  1504. DWORD cNonTextObjectsToExtend;
  1505. BOOL fDummy;
  1506. if (plssubl->plsdnFirst == NULL)
  1507. {
  1508. return lserrNone;
  1509. }
  1510. Assert(plssubl->fDupInvalid == fTrue);
  1511. ScaleDownLevel(plssubl, &fDummy, &(plsc->plslineCur->fCollectVisual));
  1512. CollectGroupChunkPurpose = (fCompress) ? CollectSublinesForCompression : CollectSublinesForJustification;
  1513. InitGroupChunkExt(plssubl->plschunkcontext, iobjText, &grchunkext); /* prepare GRCHUNKEXT */
  1514. lserr = CollectTextGroupChunk(pdnFirst, cpLim, CollectGroupChunkPurpose, &grchunkext);
  1515. if (lserr != lserrNone)
  1516. return lserr;
  1517. if (FDnodeBeforeCpLim(grchunkext.plsdnNext, cpLim)) // more than one GroupChunk -
  1518. {
  1519. return MatchPresSubline(plssubl); // cancel Expansion
  1520. }
  1521. dupAvailable = dup - grchunkext.dupNonTextTotal;
  1522. if (dupAvailable < 0) // input dup is wrong -
  1523. {
  1524. return MatchPresSubline(plssubl); // cancel Expansion
  1525. }
  1526. durColumnMax = UrFromUp(lstflow, pdevres, dup); // get dur by scaling back
  1527. lserr = AdjustText(lskjust, durColumnMax, grchunkext.durTotal - grchunkext.durTrailing,
  1528. dupAvailable,
  1529. &(grchunkext.lsgrchnk),
  1530. &(grchunkext.posichnkBeforeTrailing), lstflow,
  1531. fCompress, // compress?
  1532. grchunkext.cNonTextObjects,
  1533. fTrue, // fSuppressWiggle
  1534. fTrue, // fExact
  1535. fFalse, // fForcedBreak
  1536. fFalse, // fSuppressTrailingSpaces
  1537. &dupText, &dupTail, &dupToAddToNonTextObjects, &cNonTextObjectsToExtend);
  1538. if (lserr != lserrNone)
  1539. return lserr;
  1540. if (cNonTextObjectsToExtend != 0 && dupToAddToNonTextObjects > 0)
  1541. {
  1542. WidenNonTextObjects(&grchunkext, dupToAddToNonTextObjects, cNonTextObjectsToExtend);
  1543. }
  1544. // fLastOnLine is always false on lower levels
  1545. lserr = CalcPresChunk(plsc, plssubl->plsdnFirst, plssubl->plsdnLastDisplay,
  1546. CollectGroupChunkPurpose, fCompress, lskjust, fFalse);
  1547. plssubl->fDupInvalid = fFalse;
  1548. return lserrNone;
  1549. }