|
|
#include <limits.h>
#include "prepdisp.h"
#include "lsc.h"
#include "lsline.h"
#include "lssubl.h"
#include "iobj.h"
#include "lstxtjst.h"
#include "lstxttab.h"
#include "lsgrchnk.h"
#include "posichnk.h"
#include "chnutils.h"
#include "tabutils.h"
#include "lsdnode.h"
#include "zqfromza.h"
#include "lsdevice.h"
#include "lssubset.h"
#include "lsffi.h"
#include "iobjln.h"
#include "txtconst.h"
#include "lskalign.h"
#include "dninfo.h"
typedef enum /* types of TextGroupChunk walls */ { LineBegin, LineEnd, Tab, Pen, } KWALL;
typedef struct { KWALL kwall; /* wall type */ PLSDNODE pdn; /* tab or pen dnode, PLINEBEGIN for LineBegin */ LSKTAB lsktab; /* if type is Tab - kind of tab */ WCHAR wchCharTab; /* point character if lsktab == lsktChar */ long upTab; /* scaled tab position */ } GrpChnkWall;
static BOOL DnodeHasSublineForMe(PLSDNODE pdn, BOOL fLineCompressed); static void ScaleDownLevel(PLSSUBL plssubl, BOOL* pfAnySublines, BOOL* pfCollectVisual); static LSERR SetJustificationForLastGroupChunk(PLSLINE plsline, GrpChnkWall LastWall, LSKJUST* plskj, LSKALIGN* plskalign); static LSERR CalcPresAutonumbers(PLSLINE plsline, PLSDNODE* pdnStartMainText); static void FindWallToCollectSublinesAfter(PLSDNODE pdnFirst, LSCP cpLim, BOOL fLineCompressed, PLSDNODE* ppdnLastWall); static LSERR GetDistanceToTabPoint(GRCHUNKEXT* pgrchunkext, LSCP cpLim, LSKTAB lsktab, WCHAR wchCharTab, PLSDNODE pdnFirst, long* pdupToDecimal); static void WidenNonTextObjects(GRCHUNKEXT* pgrchunkext, long dupToAdd, DWORD cObjects); static void ConvertAutoTabToPen(PLSLINE plsline, PLSDNODE pdnAutoDecimalTab); static LSERR CalcPresForDnodeWithSublines(PLSC plsc, PLSDNODE pdn, BOOL fLineCompressed, LSKJUST lskj, BOOL fLastOnLine); static LSERR CalcPresChunk(PLSC plsc, PLSDNODE pdnFirst, PLSDNODE pdnLim, COLLECTSUBLINES CollectGroupChunkPurpose, BOOL fLineCompressed, LSKJUST lskj, BOOL fLastOnLine); static void UpdateUpLimUnderline(PLSLINE plsline, long dupTail); static LSERR PrepareLineForDisplay(PLSLINE plsline);
#define PLINEBEGIN ((void *)(-1))
#define FIsWall(p, cpLim) (!FDnodeBeforeCpLim(p, cpLim) || p->fTab || FIsDnodePen(p))
#define FIsDnodeNormalPen(plsdn) (FIsDnodePen(plsdn) && (!(plsdn)->fAdvancedPen))
#define FCollinearTflows(t1, t2) (((t1) & fUVertical) == ((t2) & fUVertical))
// %%Function: DnodeHasSublineForMe
// %%Contact: victork
//
// Is there relevant subline in this dnode?
static BOOL DnodeHasSublineForMe(PLSDNODE pdn, BOOL fLineCompressed) { BOOL fSublineFound = fFalse; if (FIsDnodeReal(pdn) && pdn->u.real.pinfosubl != NULL) { if (pdn->u.real.pinfosubl->fUseForCompression && fLineCompressed) { fSublineFound = fTrue; } if (pdn->u.real.pinfosubl->fUseForJustification && !fLineCompressed) { fSublineFound = fTrue; } } return fSublineFound; }
// %%Function: ScaleDownLevel
// %%Contact: victork
//
/*
* Scales all non-text objects on the level(s). * * If the level (meaning subline) contains dnode(s) which submitted sublines for compression * or expansion, ScaleDownLevel reports the fact and calls itself for submitted sublines. * This strategy relyes on the fact that ScaleDownLevel is idempotent procedure. Some sublines * will be scaled down twice - let that be. * * Two additional questions are answered - whether there are some submitted sublines and * whether there is a reason go VisualLine (underlining, shading, borders on lower levels). */
static void ScaleDownLevel(PLSSUBL plssubl, BOOL* pfAnySublines, BOOL* pfCollectVisual) { const PLSC plsc = plssubl->plsc; LSTFLOW lstflow = plssubl->lstflow; const DWORD iobjText = IobjTextFromLsc(&(plsc->lsiobjcontext)); LSDEVRES* pdevres = &(plsc->lsdocinf.lsdevres); PLSDNODE pdn = plssubl->plsdnFirst; DWORD i; BOOL fDummy; BOOL fSeeReasonForVisualLine = fFalse; while (pdn != NULL) /* don't care about break */ { if (FIsDnodeReal(pdn)) { if (pdn->u.real.lschp.fUnderline || pdn->u.real.lschp.fShade || pdn->u.real.lschp.fBorder) { fSeeReasonForVisualLine = fTrue; } if (IdObjFromDnode(pdn) == iobjText) { if (pdn->fTab) pdn->u.real.dup = UpFromUr(lstflow, pdevres, pdn->u.real.objdim.dur); } else { if (!pdn->fRigidDup) { pdn->u.real.dup = UpFromUr(lstflow, pdevres, pdn->u.real.objdim.dur); } if (pdn->u.real.pinfosubl != NULL) { *pfAnySublines = fTrue;
for (i = 0; i < pdn->u.real.pinfosubl->cSubline; i++) { ScaleDownLevel((pdn->u.real.pinfosubl->rgpsubl)[i], &fDummy, pfCollectVisual); } } } } else if (FIsDnodePen(pdn)) { pdn->u.pen.dup = UpFromUr(lstflow, pdevres, pdn->u.pen.dur); } else { // Borders are rigidDup always - no scaling down
// we'll try to "undo" the moving at display time at the main level if
// fUnderlineTrailSpacesRM is on. So, after prepdisp we want none or only one
// fBorderMovedFromTrailingArea flag remain and the meaning of the flag is:
// I am the border that should be moved back into trailing spaces.
if (pdn->fBorderMovedFromTrailingArea) { if (!FIsSubLineMain(pdn->plssubl) || !plsc->lsadjustcontext.fUnderlineTrailSpacesRM) { pdn->fBorderMovedFromTrailingArea = fFalse; } } }
pdn = pdn->plsdnNext; } if (fSeeReasonForVisualLine && !plssubl->fMain) { *pfCollectVisual = fTrue; } }
// %%Function: FindWallToCollectSublinesAfter
// %%Contact: victork
//
// Finds the last wall - wall after which we will start to use submitted subllines.
// If there are no sublines to participate in justification, pdnLastWall is set to null,
// else it points to the last wall (tab, pen or PLINEBEGIN).
static void FindWallToCollectSublinesAfter(PLSDNODE pdnFirst, LSCP cpLim, BOOL fLineCompressed, PLSDNODE* ppdnLastWall) { PLSDNODE pdn; BOOL fSublineFound;
// Find last tab.
*ppdnLastWall = PLINEBEGIN; pdn = pdnFirst;
while (FDnodeBeforeCpLim(pdn, cpLim)) { if (FIsDnodeReal(pdn)) { if (pdn->fTab) { *ppdnLastWall = pdn; } } else /* pen */ if (!FIsDnodeBorder(pdn) && !pdn->fAdvancedPen) // and not advance pen or border
{ *ppdnLastWall = pdn; } pdn = pdn->plsdnNext; }
// OK, last groupchunk starts with a tab or there is only one groupchunk on the line.
// Are there submitted sublines of our compression/expansion type after it?
fSublineFound = fFalse; if (*ppdnLastWall == PLINEBEGIN) { pdn = pdnFirst; } else { pdn = (*ppdnLastWall)->plsdnNext; }
while (FDnodeBeforeCpLim(pdn, cpLim)) { fSublineFound |= DnodeHasSublineForMe(pdn, fLineCompressed); pdn = pdn->plsdnNext; }
if (!fSublineFound) { *ppdnLastWall = NULL; // don't need last tab
} return; }
// %%Function: CalcPresAutonumbers
// %%Contact: victork
//
/*
* Scales dup for autonumbering dnodes, calls CalcPres for autonumbering object. * * We want to have main line start exactly on upStartMainText. To achive that we play with * the width of "white space" dnode, which today contains a tab (usually) or a space. * (This dnode is pdnWhiteSpace in the code.) If it is not present, we change width of autonumbering * object itself. We don't want one of them go negative,so sometimes rounding errors force us * to move start of the main text to the right. */
static LSERR CalcPresAutonumbers(PLSLINE plsline, PLSDNODE* pdnStartMainText) { LSERR lserr; const PLSC plsc = plsline->lssubl.plsc; LSTFLOW lstflow = plsline->lssubl.lstflow; LSDEVRES* pdevres = &(plsc->lsdocinf.lsdevres); PLSDNODE pdn, pdnObject, pdnWhiteSpace, pdnToAdjust, pdnAfterAutonumbers;
long dupAdjust, dupClosingBorder = 0; long dupAutonumbering = 0;
plsline->upStartAutonumberingText = UpFromUr(lstflow, pdevres, plsc->lsadjustcontext.urStartAutonumberingText);
// Find the first dnode after autonumbering sequence
// First find the first dnode with positive cpFirst
pdn = plsline->lssubl.plsdnFirst;
Assert(pdn != NULL && FIsNotInContent(pdn)); pdnAfterAutonumbers = pdn->plsdnNext;
while (pdnAfterAutonumbers != NULL && FIsNotInContent(pdnAfterAutonumbers)) { pdn = pdnAfterAutonumbers; pdnAfterAutonumbers = pdn->plsdnNext; } // pdnAfterAutonumbers is first dnode in content (with positive cpFirst). It can be NULL.
// But it is not the first dnode after autonumbering sequence if autodecimal tab is present
if (plsc->lsadjustcontext.fAutodecimalTabPresent) { Assert(FIsDnodeReal(pdn) && pdn->fTab); pdnAfterAutonumbers = pdn; }
// Now go againg through autonumbering sequence
// process opening border
pdn = plsline->lssubl.plsdnFirst; if (FIsDnodeBorder(pdn)) { Assert(pdn->cpFirst < 0); Assert(pdn->fOpenBorder); pdnObject = pdn->plsdnNext; dupAutonumbering += pdn->u.pen.dup; } else { pdnObject = pdn; } // process B&N object
Assert(pdnObject != NULL && pdnObject->cpFirst < 0); // B&N object should be there
Assert(FIsDnodeReal(pdnObject)); // scale down dup from dur for the first dnode
pdnObject->u.real.dup = UpFromUr(lstflow, pdevres, pdnObject->u.real.objdim.dur); dupAutonumbering += pdnObject->u.real.dup;
pdn = pdnObject->plsdnNext; Assert(pdn != NULL); // line must contain something after B&N dnodes
// process "white space" dnode
if (pdn != pdnAfterAutonumbers && FIsDnodeReal(pdn)) { pdnWhiteSpace = pdn; dupAutonumbering += pdnWhiteSpace->u.real.dup; pdnToAdjust = pdnWhiteSpace; pdn = pdnWhiteSpace->plsdnNext; } else { pdnWhiteSpace = NULL; pdnToAdjust = pdnObject; }
Assert(pdn != NULL); // line must contain something after B&N dnodes
// process closing border
if (pdn != pdnAfterAutonumbers) { Assert(FIsDnodeBorder(pdn)); Assert(!pdn->fOpenBorder);
dupClosingBorder = pdn->u.pen.dup; dupAutonumbering += dupClosingBorder; pdn = pdn->plsdnNext; }
Assert(pdn == pdnAfterAutonumbers);
*pdnStartMainText = pdn; // change dup of the tab or object dnode to ensure exact main text alignment
dupAdjust = plsline->upStartMainText - plsline->upStartAutonumberingText - dupAutonumbering; pdnToAdjust->u.real.dup += dupAdjust; if (pdnToAdjust->u.real.dup < 0) { // Rounding errors result in negative dup - better to move starting point of the main line.
// It can lead to the nasty situation of right margin to the left of the line beginning in
// theory, but not in practice. This problem is ignored then.
plsline->upStartMainText -= pdnToAdjust->u.real.dup; pdnToAdjust->u.real.dup = 0; }
// do CalcPres for the autonumbering object - it's always lskjNone and not last object on the line
lserr = (*plsc->lsiobjcontext.rgobj[pdnObject->u.real.lschp.idObj].lsim.pfnCalcPresentation) (pdnObject->u.real.pdobj, pdnObject->u.real.dup, lskjNone, fFalse); if (lserr != lserrNone) return lserr;
if (pdnWhiteSpace != NULL) { plsline->upLimAutonumberingText = plsline->upStartMainText - pdnWhiteSpace->u.real.dup - dupClosingBorder;
// If "white space" dnode is not a tab, dup should be set in it.
if (!pdnWhiteSpace->fTab) { // It's always lskjNone and not last object on the line for white space dnode
lserr = (*plsc->lsiobjcontext.rgobj[pdnWhiteSpace->u.real.lschp.idObj].lsim.pfnCalcPresentation) (pdnWhiteSpace->u.real.pdobj, pdnWhiteSpace->u.real.dup, lskjNone, fFalse); if (lserr != lserrNone) return lserr; } } else { plsline->upLimAutonumberingText = plsline->upStartMainText - dupClosingBorder; } return lserrNone; }
// %%Function: SetJustificationForLastGroupChunk
// %%Contact: victork
//
// Changes lskj and lskalign for the last GC if it should be done
// If not, it's OK to leave these parameters unchanged - so they are kind of I/O
//
// We adjust all group chunks except maybe the last one with lskjNone, so guestion is about
// last GroupChunk only.
//
// We do some tricks with justification mode at the end of line, and the answer depends on
// kind of last tab, end of paragraph, etc.
//
static LSERR SetJustificationForLastGroupChunk(PLSLINE plsline, GrpChnkWall LastWall, LSKJUST* plskj, LSKALIGN* plskalign)
{ LSERR lserr; const PLSC plsc = plsline->lssubl.plsc; LSKJUST lskjPara = plsc->lsadjustcontext.lskj; LSKALIGN lskalignPara = plsc->lsadjustcontext.lskalign; ENDRES endr = plsline->lslinfo.endr; BOOL fJustify;
// no justification intended - lskj remains None, lskalign unchanged
if ((lskjPara == lskjNone || lskjPara == lskjSnapGrid) && lskalignPara == lskalLeft) { return lserrNone; } // Line ends in a normal way - we apply justification, lskalign unchanged
if (endr == endrNormal || endr == endrHyphenated) { *plskj = lskjPara; return lserrNone; } // break-through tab kills justification, alignment games
if (FBreakthroughLine(plsc)) { return lserrNone; }
// if last Left Wall is non-left tab, justification is off too
if (LastWall.kwall == Tab && LastWall.lsktab != lsktLeft) { // we used to return here
// Now we want to give Word a chance to change lskalign from Right to Left for
// the last line in paragraph after text box.
// REVIEW (Victork) Should we call pfnFGetLastLineJustification always?
lskjPara = lskjNone; }
// What's the matter behind the callback.
//
// They say: no full justification for the last line in paragraph. What does this exactly mean?
// For example, Latin and FE word make different decisions for endrEndSection line.
// Let's ask.
//
// Additional parameter is added to cover the behavior full-justified text wrapping a textbox (bug 682)
// A lone word should be aligned to the right to create a full-justified page, but not at the end of
// the paragraph.
lserr = (*plsc->lscbk.pfnFGetLastLineJustification)(plsc->pols, lskjPara, lskalignPara, endr, &fJustify, plskalign); if (lserr != lserrNone) return lserr;
if (fJustify) { *plskj = lskjPara; } return lserrNone; }
// %%Function: GetDistanceToTabPoint
// %%Contact: victork
//
/*
* Calculate DistanceToTabPoint given GrpChnk and first Dnode * * TabPoint is decimal point for the decimal tab, wchCharTab for character tab */ static LSERR GetDistanceToTabPoint(GRCHUNKEXT* pgrchunkext, LSCP cpLim, LSKTAB lsktab, WCHAR wchCharTab, PLSDNODE pdnFirst, long* pdupToTabPoint) { LSERR lserr; DWORD igrchnk; /* # of dnodes before dnode with the point */ long dupToPointInsideDnode; PLSDNODE pdnTabPoint;
if (pgrchunkext->durTotal == 0) { *pdupToTabPoint = 0; return lserrNone; }
lserr = CollectTextGroupChunk(pdnFirst, cpLim, CollectSublinesForDecimalTab, pgrchunkext); if (lserr != lserrNone) return lserr;
if (lsktab == lsktDecimal) { lserr = LsGetDecimalPoint(&(pgrchunkext->lsgrchnk), lsdevPres, &igrchnk, &dupToPointInsideDnode); } else { Assert(lsktab == lsktChar); lserr = LsGetCharTab(&(pgrchunkext->lsgrchnk), wchCharTab, lsdevPres, &igrchnk, &dupToPointInsideDnode); } if (lserr != lserrNone) return lserr; if (igrchnk == ichnkOutside) // no TabPoint in the whole grpchnk
{ // we say: pretend it is right after last dnode (in logical sequence)
pdnTabPoint = pgrchunkext->plsdnLastUsed; dupToPointInsideDnode = DupFromDnode(pdnTabPoint); } else { pdnTabPoint = pgrchunkext->plschunkcontext->pplsdnChunk[igrchnk]; } // We now have the distance between TabPoint and the beginning of the dnode containing it.
// FindPointOffset will add the dup's of all dnodes before that dnode.
FindPointOffset(pdnFirst, lsdevPres, LstflowFromDnode(pdnFirst), CollectSublinesForDecimalTab, pdnTabPoint, dupToPointInsideDnode, pdupToTabPoint); return lserrNone; }
// %%Function: WidenNonTextObjects
// %%Contact: victork
//
/*
* Add dupToAddToNonTextObjects to the width of first cNonTextObjectsToExtend in the GroupChunk */ static void WidenNonTextObjects(GRCHUNKEXT* pgrchunkext, long dupToAdd, DWORD cObjects) { PLSDNODE pdn;
long dupAddToEveryone; long dupDistributeToFew; long dupAddToThis; long dupCurrentSum;
DWORD cObjectsLeft, i;
Assert(cObjects != 0); Assert(dupToAdd > 0);
dupAddToEveryone = dupToAdd / cObjects; dupDistributeToFew = dupToAdd - (dupAddToEveryone * cObjects);
cObjectsLeft = cObjects; dupCurrentSum = 0;
/*
* Following loop tries to distribute remaining dupDistributeToFew pixels evenly. * * Algorithm would be easy if fractions are allowed and you can see it in comments; * The actual algorithm avoids fractions by multiplying everything by cObjects */
i = 0; while (cObjectsLeft > 0) { Assert(i < pgrchunkext->cNonTextObjects); pdn = (pgrchunkext->pplsdnNonText)[i]; Assert(pdn != NULL && FIsDnodeReal(pdn) /* && IdObjFromDnode(pdn) != iobjText */ );
if ((pgrchunkext->pfNonTextExpandAfter)[i]) { dupAddToThis = dupAddToEveryone;
dupCurrentSum += dupDistributeToFew; /* currentSum += Distribute / cObjects; */ if (dupCurrentSum >= (long)cObjects) /* if (currentSum >= 1) */ { dupAddToThis ++; dupCurrentSum -= (long)cObjects; /* currentSum--; */ } pdn->u.real.dup += dupAddToThis;
cObjectsLeft --; } i++; }
return; }
// %%Function: ConvertAutoTabToPen
// %%Contact: victork
//
static void ConvertAutoTabToPen(PLSLINE plsline, PLSDNODE pdnAutoDecimalTab) { long dup, dur;
Assert(pdnAutoDecimalTab->fTab); /* it's still a tab */
dup = pdnAutoDecimalTab->u.real.dup; dur = pdnAutoDecimalTab->u.real.objdim.dur;
pdnAutoDecimalTab->klsdn = klsdnPenBorder; pdnAutoDecimalTab->fAdvancedPen = fFalse; pdnAutoDecimalTab->fTab = fFalse; pdnAutoDecimalTab->icaltbd = 0; pdnAutoDecimalTab->u.pen.dup = dup; pdnAutoDecimalTab->u.pen.dur = dur; pdnAutoDecimalTab->u.pen.dvp = 0; pdnAutoDecimalTab->u.pen.dvr = 0; plsline->fNonRealDnodeEncounted = fTrue; }
// %%Function: CalcPresForDnodeWithSublines
// %%Contact: victork
//
static LSERR CalcPresForDnodeWithSublines(PLSC plsc, PLSDNODE pdn, BOOL fLineCompressed, LSKJUST lskj, BOOL fLastOnLine) { PLSSUBL* rgpsubl; DWORD i; LSTFLOW lstflow; // dummy parameter
LSERR lserr; long dupSubline; long dupDnode = 0; COLLECTSUBLINES CollectGroupChunkPurpose;
Assert(DnodeHasSublineForMe(pdn, fLineCompressed)); // calculate dup for dnode with sublines that took part in justification
if (fLineCompressed) { CollectGroupChunkPurpose = CollectSublinesForCompression; } else { CollectGroupChunkPurpose = CollectSublinesForJustification; }
rgpsubl = pdn->u.real.pinfosubl->rgpsubl; for (i = 0; i < pdn->u.real.pinfosubl->cSubline; i++) { // fLastOnLine is always false on lower levels
lserr = CalcPresChunk(plsc, rgpsubl[i]->plsdnFirst, rgpsubl[i]->plsdnLastDisplay, CollectGroupChunkPurpose, fLineCompressed, lskj, fFalse); if (lserr != lserrNone) return lserr; LssbGetDupSubline(rgpsubl[i], &lstflow, &dupSubline); dupDnode += dupSubline; (rgpsubl[i])->fDupInvalid = fFalse; }
// fill dup and call CalcPresentation
pdn->u.real.dup = dupDnode;
lserr = (*plsc->lsiobjcontext.rgobj[pdn->u.real.lschp.idObj].lsim.pfnCalcPresentation) (pdn->u.real.pdobj, dupDnode, lskj, fLastOnLine); if (lserr != lserrNone) return lserr; return lserrNone; }
// %%Function: CalcPresChunk
// %%Contact: victork
//
/*
* Calls CalcPresentation for all non-text objects on the chunk. * That means 1) all dnodes in all GroupChunks (including dnodes in submitted sublines) * 2) all dnodes that have submitted sublines * * Foreign object on the upper level, which is followed only by trailing spaces, * should be called with fLastOnLine == fTrue. * Input boolean says whether the input groupchunk is the last on line. * * Sets dup for justified sublines */ static LSERR CalcPresChunk(PLSC plsc, PLSDNODE pdnFirst, PLSDNODE pdnLast, COLLECTSUBLINES CollectGroupChunkPurpose, BOOL fLineCompressed, LSKJUST lskj, BOOL fLastOnLine) { LSERR lserr; const DWORD iobjText = IobjTextFromLsc(&(plsc->lsiobjcontext)); BOOL fCollecting; PLSDNODE pdn; long dupTailDnode; // dummy parameter - will not use
DWORD cNumOfTrailers;
fCollecting = (CollectGroupChunkPurpose != CollectSublinesNone);
Assert(pdnFirst != NULL); Assert(pdnLast != NULL); pdn = pdnLast;
// go backwards to switch fLastOnLine off once we are not in trailing spaces
for (;;) { if (FIsDnodeReal(pdn)) if (IdObjFromDnode(pdn) == iobjText) { if (fLastOnLine == fTrue) { GetTrailInfoText(pdn->u.real.pdobj, pdn->dcp, &cNumOfTrailers, &dupTailDnode);
if (cNumOfTrailers < pdn->dcp) { fLastOnLine = fFalse; // trailing spaces stop here
} } } else { if (fCollecting && DnodeHasSublineForMe(pdn, fLineCompressed)) { lserr = CalcPresForDnodeWithSublines(plsc, pdn, fLineCompressed, lskj, fLastOnLine); if (lserr != lserrNone) return lserr; } else { lserr = (*plsc->lsiobjcontext.rgobj[pdn->u.real.lschp.idObj].lsim.pfnCalcPresentation) (pdn->u.real.pdobj, pdn->u.real.dup, lskj, fLastOnLine); if (lserr != lserrNone) return lserr; } fLastOnLine = fFalse; }
if (pdn == pdnFirst) { break; } pdn = pdn->plsdnPrev;
Assert(pdn != NULL); // we'll encounter pdnFirst first
} return lserrNone; }
// %%Function: UpdateUpLimUnderline
// %%Contact: victork
//
/*
* Change upLimUnderline to underline trailing spaces, but not EOP. * Notice that from now on upLimUnderline doesn't equals upStartTrailing anymore */ static void UpdateUpLimUnderline(PLSLINE plsline, long dupTail) { PLSDNODE pdnLast; plsline->upLimUnderline += dupTail;
// Now EOPs - they are alone in the last dnode or have some borders around them
if (plsline->lslinfo.endr == endrEndPara || plsline->lslinfo.endr == endrAltEndPara || plsline->lslinfo.endr == endrEndParaSection || plsline->lslinfo.endr == endrSoftCR) { pdnLast = plsline->lssubl.plsdnLastDisplay;
Assert(FIsDnodeReal(pdnLast)); // no borders in trailing spaces area
Assert(pdnLast->dcp == 1); Assert(pdnLast->u.real.dup <= dupTail); plsline->upLimUnderline -= pdnLast->u.real.dup;
pdnLast = pdnLast->plsdnPrev; }
// This option extends underlining only up to the right margin
if (plsline->upLimUnderline > plsline->upRightMarginJustify) { plsline->upLimUnderline = plsline->upRightMarginJustify; } }
// %%Function: PrepareLineForDisplayProc
// %%Contact: victork
//
/*
* PrepareLineForDisplayProc fills in the dup's in dnode list and lsline * * Input dnode list consists of "normal dnode list" of dnodes with positive non-negative cp, * which can be preceded (in this order) by B&N sequence either and/or one Autotab dnode. * * B&N sequence is OpeningBorder+AutonumberingObject+TabOrSpace+ClosingBorder. * ClosingBorder or both OpeningBorder and ClosingBorder can be missing. TabOrSpace can be * missing too. B&N sequence starts at urStartAutonumberingText and ends at urStartMainText. * Tab in B&N sequence should not be resolved in a usual way. * * Autotab dnode has negative cpFirst, but starts at urStartMainText. It is to be resolved in * a usual way and then be replaced by a pen dnode. */
LSERR PrepareLineForDisplayProc(PLSLINE plsline) {
LSERR lserr; const PLSC plsc = plsline->lssubl.plsc; const DWORD iobjText = IobjTextFromLsc(&(plsc->lsiobjcontext)); LSDEVRES* pdevres = &(plsc->lsdocinf.lsdevres); LSTFLOW lstflow = plsline->lssubl.lstflow; /* text flow of the line */ BOOL fVertical = lstflow & fUVertical;
long dupText, dupTail, dupTailDnode; DWORD cNumOfTrailers; PLSDNODE pdn; BOOL fLastOnLine;
DWORD i; PDOBJ pdobj[txtobjMaxM]; // quick group chunk
Assert(FIsLSLINE(plsline));
// Next assert means that client should destroy line immediately after creating it
// if fDisplay is set to fFalse in LsSetDoc.
Assert(FDisplay(plsc));
if (!plsline->lssubl.fDupInvalid) /* line has been prepared earlier */ return lserrNone;
Assert(plsc->lsstate == LsStateFree); Assert(plsc->plslineCur == plsline);
plsc->lsstate = LsStatePreparingForDisplay;
// first try to recognize quick cases, call slow PredDisp otherwise
if (plsc->lsadjustcontext.lskj != lskjNone || plsc->lsadjustcontext.lskalign != lskalLeft || plsc->lsadjustcontext.lsbrj != lsbrjBreakJustify || plsc->lsadjustcontext.fNominalToIdealEncounted || plsc->lsadjustcontext.fSubmittedSublineEncounted || plsline->fNonRealDnodeEncounted || plsline->lssubl.plsdnFirst == NULL || FIsNotInContent(plsline->lssubl.plsdnFirst)) { return PrepareLineForDisplay(plsline); }
if (plsc->lsdocinf.fPresEqualRef && !FSuspectDeviceDifferent(PlnobjFromLsline(plsline, iobjText))) { // Trident quick case - no need to scale down. Dups are already set in text dnodes.
// go through dnode list to calculate dupTrail and CalcPres foreign objects
pdn = plsline->lssubl.plsdnLastDisplay; dupTail = 0; fLastOnLine = fTrue; while (pdn != NULL && IdObjFromDnode(pdn) == iobjText) { Assert(pdn->u.real.dup == pdn->u.real.objdim.dur);
GetTrailInfoText(pdn->u.real.pdobj, pdn->dcp, &cNumOfTrailers, &dupTailDnode);
dupTail += dupTailDnode;
if (cNumOfTrailers < pdn->dcp) { fLastOnLine = fFalse; // trailing spaces stop here
break; // text is the last on the line
}
pdn = pdn->plsdnPrev; }
// dupTail is calculated, we still should call pfnCalcPresentation for foreing objects
if (plsc->lsadjustcontext.fForeignObjectEncounted) {
while (pdn != NULL) { Assert(pdn->u.real.dup == pdn->u.real.objdim.dur); if (IdObjFromDnode(pdn) != iobjText) { lserr = (*plsc->lsiobjcontext.rgobj[pdn->u.real.lschp.idObj].lsim.pfnCalcPresentation) (pdn->u.real.pdobj, pdn->u.real.dup, lskjNone, fLastOnLine); if (lserr != lserrNone) { plsc->lsstate = LsStateFree; return lserr; }
fLastOnLine = fFalse; // only the first coulsd be the last on the line
} pdn = pdn->plsdnPrev; } } plsline->lssubl.fDupInvalid = fFalse; plsline->upRightMarginJustify = plsc->lsadjustcontext.urRightMarginJustify; plsline->upStartMainText = plsc->lsadjustcontext.urStartMainText; plsline->upStartAutonumberingText = plsline->upStartMainText; plsline->upLimAutonumberingText = plsline->upStartMainText; plsline->upLimLine = plsline->lssubl.urCur; plsline->upStartTrailing = plsline->upLimLine - dupTail; plsline->upLimUnderline = plsline->upStartTrailing;
plsline->fCollectVisual = fFalse;
if (plsc->lsadjustcontext.fUnderlineTrailSpacesRM && plsline->upLimUnderline < plsline->upRightMarginJustify) { UpdateUpLimUnderline(plsline, dupTail); } plsc->lsstate = LsStateFree; return lserrNone; }
if ((plsc->grpfManager & fFmiPresExactSync) != 0 && !plsc->lsadjustcontext.fForeignObjectEncounted && !plsc->lsadjustcontext.fNonLeftTabEncounted && plsline->lssubl.plsdnLastDisplay != NULL && // empty line is not a quick case ;(
FQuickScaling(PlnobjFromLsline(plsline, iobjText), fVertical, plsline->lssubl.urCur - plsc->lsadjustcontext.urStartMainText)) { // Looks like Word quick case
// We can still go slow way if all trailing spaces are not in one dnode
if (plsline->lslinfo.endr == endrEndPara) { Assert(FIsDnodeReal(plsline->lssubl.plsdnLastDisplay)); Assert(plsline->lssubl.plsdnLastDisplay->dcp == 1); pdn = plsline->lssubl.plsdnLastDisplay->plsdnPrev;
if (pdn != NULL) { GetTrailInfoText(pdn->u.real.pdobj, pdn->dcp, &cNumOfTrailers, &dupTailDnode); if (cNumOfTrailers > 0) { // There are spaces before EOP - go slow way
return PrepareLineForDisplay(plsline); } }
cNumOfTrailers = 1; } else { pdn = plsline->lssubl.plsdnLastDisplay; GetTrailInfoText(pdn->u.real.pdobj, pdn->dcp, &cNumOfTrailers, &dupTailDnode); if (cNumOfTrailers == pdn->dcp) { // We can't be sure all spaces are in this dnode - forget it then
return PrepareLineForDisplay(plsline); } }
// we are sure now that all cNumOfTrailers trailing spaces are in the last dnode
// fill standard output part, upStartMainText will be used below
plsline->lssubl.fDupInvalid = fFalse; plsline->fCollectVisual = fFalse;
plsline->upRightMarginJustify = UpFromUr(lstflow, pdevres, plsc->lsadjustcontext.urRightMarginJustify); plsline->upStartMainText = UpFromUr(lstflow, pdevres, plsc->lsadjustcontext.urStartMainText); plsline->upStartAutonumberingText = plsline->upStartMainText; plsline->upLimAutonumberingText = plsline->upStartMainText; if (!plsc->lsadjustcontext.fTabEncounted) { // Very nice, we have only one groupchunk to collect
for (pdn = plsline->lssubl.plsdnFirst, i = 0;;) { Assert(FIsDnodeReal(pdn)); Assert(IdObjFromDnode(pdn) == iobjText);
// i never gets outside of pdobj array.
// Text makes sure in FQuickscaling
Assert(i < txtobjMaxM); pdobj[i] = pdn->u.real.pdobj;
i++;
if (pdn == plsline->lssubl.plsdnLastDisplay) { break; }
pdn = pdn->plsdnNext; }
QuickAdjustExact(&(pdobj[0]), i, cNumOfTrailers, fVertical, &dupText, &dupTail);
plsline->upRightMarginJustify = UpFromUr(lstflow, pdevres, plsc->lsadjustcontext.urRightMarginJustify); plsline->upStartMainText = UpFromUr(lstflow, pdevres, plsc->lsadjustcontext.urStartMainText); plsline->upStartAutonumberingText = plsline->upStartMainText; plsline->upLimAutonumberingText = plsline->upStartMainText; plsline->upLimLine = plsline->upStartMainText + dupText; plsline->upStartTrailing = plsline->upLimLine - dupTail; plsline->upLimUnderline = plsline->upStartTrailing;
plsline->fCollectVisual = fFalse;
if (plsc->lsadjustcontext.fUnderlineTrailSpacesRM && plsline->upLimUnderline < plsline->upRightMarginJustify) { UpdateUpLimUnderline(plsline, dupTail); } plsc->lsstate = LsStateFree; return lserrNone; } else { // Tabs are present, but they all are left tabs
pdn = plsline->lssubl.plsdnFirst; plsline->upLimLine = plsline->upStartMainText;
// Do one QuickGroupChunk after another, moving plsline->upLimLine
for (;;) { // loop body: Collect next QuickGroupChunk, deal with it, exit after last one
for (i = 0;;) { Assert(FIsDnodeReal(pdn)); Assert(IdObjFromDnode(pdn) == iobjText);
if (pdn->fTab) { break; }
Assert(i < txtobjMaxM); pdobj[i] = pdn->u.real.pdobj;
i++;
if (pdn == plsline->lssubl.plsdnLastDisplay) { break; }
pdn = pdn->plsdnNext; }
Assert(pdn == plsline->lssubl.plsdnLastDisplay || pdn->fTab);
if (pdn->fTab) { long upTabStop; if (i == 0) { dupText = 0; dupTail = 0; } else { QuickAdjustExact(pdobj, i, 0, fVertical, &dupText, &dupTail); }
Assert(plsc->lstabscontext.pcaltbd[pdn->icaltbd].lskt == lsktLeft);
upTabStop = UpFromUr(lstflow, pdevres, plsc->lstabscontext.pcaltbd[pdn->icaltbd].ur); pdn->u.real.dup = upTabStop - plsline->upLimLine - dupText; plsline->upLimLine = upTabStop;
if (pdn == plsline->lssubl.plsdnLastDisplay) { break; } pdn = pdn->plsdnNext; } else {
Assert(i != 0); QuickAdjustExact(pdobj, i, cNumOfTrailers, fVertical, &dupText, &dupTail); plsline->upLimLine += dupText;
break; } } plsline->upStartTrailing = plsline->upLimLine - dupTail; plsline->upLimUnderline = plsline->upStartTrailing;
if (plsc->lsadjustcontext.fUnderlineTrailSpacesRM && plsline->upLimUnderline < plsline->upRightMarginJustify) { UpdateUpLimUnderline(plsline, dupTail); } plsc->lsstate = LsStateFree; return lserrNone; } }
// Getting here means quick prepdisp haven't happen
return PrepareLineForDisplay(plsline);
}
/*
* This is slow and painstaking procedure that does everyting. * Called when QuickPrep above cannot cope. */
static LSERR PrepareLineForDisplay(PLSLINE plsline) { LSERR lserr = lserrNone; const PLSC plsc = plsline->lssubl.plsc; LSTFLOW lstflow = plsline->lssubl.lstflow; /* text flow of the subline */ const DWORD iobjText = IobjTextFromLsc(&(plsc->lsiobjcontext)); LSDEVRES* pdevres = &(plsc->lsdocinf.lsdevres); long urColumnMax = plsc->lsadjustcontext.urRightMarginJustify; long upColumnMax = UpFromUr(lstflow, pdevres, urColumnMax); LSCP cpLim = plsline->lssubl.cpLimDisplay; PLSDNODE pdnFirst = plsline->lssubl.plsdnFirst; /* the first dnode of the line */
PLSDNODE pdnAutoDecimalTab = NULL; /* NULL means - no such a thing on the line */
GRCHUNKEXT grchunkext; BOOL fEmptyGroupChunk; PLSDNODE pdnLastWall = NULL; /* last wall with submitted sublines after it */ BOOL fAnySublines = fFalse; COLLECTSUBLINES CollectGroupChunkPurpose = (plsc->lsadjustcontext.fLineCompressed) ? CollectSublinesForCompression : CollectSublinesForJustification;
// parameters to call AdjustText
LSKJUST lskj = lskjNone; /* These four will be changed only when calling */ BOOL fForcedBreak = fFalse; /* AdjustText last time on the line */ BOOL fSuppressTrailingSpaces = fFalse; /* if ever */ LSKALIGN lskalign = plsc->lsadjustcontext.lskalign; // Alignment can be changed too
long dupAvailable; BOOL fExact; BOOL fSuppressWiggle; long dupText, dupTail = 0, dupToAddToNonTextObjects; long durColumnMax; DWORD cNonTextObjectsToExtend;
PLSDNODE pdnNextFirst; /* first Dnode of the next GrpChnk */ GrpChnkWall LeftWall, RightWall; /* current TextGroupChunk walls */ long upLeftWall,urLeftWall; /* Left wall position */
long dupWall, durWall; long dupGrpChnk; long dupToTabPoint; long dupJustifyLine;
LSKTAB lsktabLast = lsktLeft; long dupLastTab = 0; long upLeftWallForCentering; PLSDNODE pdnLast;
InitGroupChunkExt(plsline->lssubl.plschunkcontext, iobjText, &grchunkext); /* prepare one GRCHUNKEXT for all */
plsline->upStartMainText = UpFromUr(lstflow, pdevres, plsc->lsadjustcontext.urStartMainText);
// set defaults incase there are no autonumbering
plsline->upStartAutonumberingText = plsline->upStartMainText; plsline->upLimAutonumberingText = plsline->upStartMainText;
// fCollectVisual can be reset to fTrue by ScaleDownLevel called here or in AdjustSubline
plsline->fCollectVisual = fFalse; if (!plsline->fAllSimpleText) { /* straighforward scaling down of non-text objects */ ScaleDownLevel(&(plsline->lssubl), &fAnySublines, &(plsline->fCollectVisual));
if (plsc->lsadjustcontext.fLineContainsAutoNumber) {
// do dup setting for autonumbers, update pdnFirst to point after it
lserr = CalcPresAutonumbers(plsline, &pdnFirst); if (lserr != lserrNone) { plsc->lsstate = LsStateFree; return lserr; } }
// If autodecimal tab is there, pdnFirst points at it - make a note.
// This tab can only be just before main text and it has negative cpFirst
// Check for NULL is needed because empty dnode list is possible in LS.
// We don't have a dnode for splat, so we'll get here with pdnFirst == NULL
// when line is (object that said delete me) + splat
if (plsc->lsadjustcontext.fAutodecimalTabPresent) { Assert(pdnFirst != NULL && FIsNotInContent(pdnFirst) && pdnFirst->fTab); // It doesn't need any special handling even having negative cpFirst
// We note it to convert to pen later
pdnAutoDecimalTab = pdnFirst; } if (fAnySublines) { // Find last tab and prepare sublines after it.
FindWallToCollectSublinesAfter(pdnFirst, cpLim, plsc->lsadjustcontext.fLineCompressed, &pdnLastWall); } }
/*
* Loop structure : While !end_of_line do * { * get next Wall (collect GrpChnk); * adjust GrpChnk; * set dup of the tab to the left of the GrpChnk; * move one Wall to the right * } * * Invariance: all dup before LeftWall are done. * upLeftWall is at the beginning of the left wall * pdnNextFirst is the dnode to start collecting next GrpChnk with */
pdnNextFirst = pdnFirst; LeftWall.kwall = LineBegin; LeftWall.pdn = PLINEBEGIN; LeftWall.lsktab = lsktLeft; // 4 lines just against asserts
LeftWall.wchCharTab = 0; LeftWall.upTab = 0; RightWall = LeftWall; upLeftWall = 0; urLeftWall = 0;
while (LeftWall.kwall != LineEnd) { /* 1. Find next wall (collect GrpChnk or skip collecting if two walls in a row)
* * Input: pdnNextFirst - first dnode after Left wall * * Output: RightWall.pdn & grchunkext. * if there is no GrpChnk some zeros in grchunkext is enough */ if (FIsWall(pdnNextFirst, cpLim)) { fEmptyGroupChunk = fTrue; RightWall.pdn = pdnNextFirst; grchunkext.durTotal = 0; grchunkext.durTextTotal = 0; grchunkext.dupNonTextTotal = 0; } else { lserr = CollectTextGroupChunk(pdnNextFirst, cpLim, CollectGroupChunkPurpose, &grchunkext); if (lserr != lserrNone) { plsc->lsstate = LsStateFree; return lserr; } if (grchunkext.lsgrchnk.clsgrchnk == 0 && grchunkext.cNonTextObjects == 0) { // only borders in this groupchunk - no need to call AdjustText
fEmptyGroupChunk = fTrue; grchunkext.durTextTotal = 0; } else { fEmptyGroupChunk = fFalse; } RightWall.pdn = grchunkext.plsdnNext; }
/*
* 2. fill in Right Wall information * * Input: RightWall.pdn * * Output: pdnNextFirst, RightWall information. */ if (!FDnodeBeforeCpLim(RightWall.pdn, cpLim)) { RightWall.kwall = LineEnd; } else { Assert(FIsWall(RightWall.pdn, cpLim)); pdnNextFirst = RightWall.pdn->plsdnNext;
if (FIsDnodePen(RightWall.pdn)) { RightWall.kwall = Pen; } else { Assert(RightWall.pdn->fTab); /* it must be a tab */
RightWall.kwall = Tab; RightWall.lsktab = plsc->lstabscontext.pcaltbd[RightWall.pdn->icaltbd].lskt; RightWall.wchCharTab = plsc->lstabscontext.pcaltbd[RightWall.pdn->icaltbd].wchCharTab; RightWall.upTab = UpFromUr(lstflow, pdevres, plsc->lstabscontext.pcaltbd[RightWall.pdn->icaltbd].ur); } }
/*
* prepare parameters for AdjustText * * Input: LeftWall, urLeftWall, upLeftWall, is_it_the_last_one * * Output: durColumnMax; lskj, dupAvailable and other input parameters for AdjustText * */
if (RightWall.kwall != LineEnd) { if (RightWall.kwall == Tab && RightWall.lsktab == lsktLeft) { // Now we know for sure what space we have for text in this groupchunk
// and can do decent job if client doesn't care about fExact
long upLeft, urLeft, upRight, urRight;
urRight = plsc->lstabscontext.pcaltbd[RightWall.pdn->icaltbd].ur; upRight = UpFromUr(lstflow, pdevres, urRight);
if (LeftWall.kwall == Tab && LeftWall.lsktab == lsktLeft) { urLeft = plsc->lstabscontext.pcaltbd[LeftWall.pdn->icaltbd].ur; upLeft = UpFromUr(lstflow, pdevres, urLeft); } else if (LeftWall.kwall == LineBegin) { urLeft = plsc->lsadjustcontext.urStartMainText; upLeft = plsline->upStartMainText; } else if (LeftWall.kwall == Pen) { /* pen - it've been scaled already, we know left wall dimensions in advance */ urLeft = urLeftWall + LeftWall.pdn->u.pen.dur; upLeft = upLeftWall + LeftWall.pdn->u.pen.dup; } else /* now non-left tabs */ { urLeft = urLeftWall; upLeft = upLeftWall; }
durColumnMax = urRight - urLeft; dupAvailable = upRight - upLeft;
Assert(durColumnMax >= 0); // dupAvailable can be < 0 here - visi optional hyphens in previous GC, for example.
// AdjustText doesn't mind, meaning it won't crush.
fSuppressWiggle = ((plsc->grpfManager & fFmiPresSuppressWiggle) != 0); fExact = ((plsc->grpfManager & fFmiPresExactSync) != 0); } else { // situation is complicated - we go the safest way.
durColumnMax = grchunkext.durTotal; dupAvailable = LONG_MAX; fExact = fTrue; fSuppressWiggle = fTrue; } } else { /* for the last GrpChnk we must to calculate durColumnMax and dupAvailable */
if (LeftWall.kwall == Tab && LeftWall.lsktab == lsktLeft) { durColumnMax = urColumnMax - plsc->lstabscontext.pcaltbd[LeftWall.pdn->icaltbd].ur; dupAvailable = UpFromUr(lstflow, pdevres, urColumnMax) - grchunkext.dupNonTextTotal - UpFromUr(lstflow, pdevres, plsc->lstabscontext.pcaltbd[LeftWall.pdn->icaltbd].ur); } else if (LeftWall.kwall == LineBegin) { durColumnMax = urColumnMax - plsc->lsadjustcontext.urStartMainText; dupAvailable = upColumnMax - plsline->upStartMainText - grchunkext.dupNonTextTotal;
// Ask AdjustText to set widths of trailing spaces to 0 only if
// It is the last groupchunk, (we actually only care for "the only one" situation)
// and its first dnode (again, we actually only care for "the only one" situation)
// submits subline for both justification and trailing spaces
// and this subline runs in the direction opposite to the line direction.
if (!fEmptyGroupChunk && FIsDnodeReal(grchunkext.plsdnFirst) && grchunkext.plsdnFirst->u.real.pinfosubl != NULL && grchunkext.plsdnFirst->u.real.pinfosubl->fUseForJustification && grchunkext.plsdnFirst->u.real.pinfosubl->fUseForTrailingArea && FCollinearTflows(((grchunkext.plsdnFirst->u.real.pinfosubl->rgpsubl)[0])->lstflow, lstflow) && ((grchunkext.plsdnFirst->u.real.pinfosubl->rgpsubl)[0])->lstflow != lstflow) { fSuppressTrailingSpaces = fTrue; } } else if (LeftWall.kwall == Pen) { /* pen - it've been scaled already, we know wall dimensions in advance */ durColumnMax = urColumnMax - urLeftWall - LeftWall.pdn->u.pen.dur; dupAvailable = UpFromUr(lstflow, pdevres, urColumnMax) - upLeftWall - LeftWall.pdn->u.pen.dup - grchunkext.dupNonTextTotal; } else /* now non-left tabs */ { durColumnMax = urColumnMax - urLeftWall; dupAvailable = UpFromUr(lstflow, pdevres, urColumnMax) - upLeftWall - grchunkext.dupNonTextTotal; } // we do some tricks with justification mode at the end of line
// alignment can change too.
lserr = SetJustificationForLastGroupChunk(plsline, LeftWall, &lskj, &lskalign); if (lserr != lserrNone) { plsc->lsstate = LsStateFree; return lserr; }
// Don't try to squeeze into RMJustify is RMBreak is infinite.
if (plsc->urRightMarginBreak >= uLsInfiniteRM) { dupAvailable = LONG_MAX; } fSuppressWiggle = ((plsc->grpfManager & fFmiPresSuppressWiggle) != 0); fExact = ((plsc->grpfManager & fFmiPresExactSync) != 0); fForcedBreak = plsline->lslinfo.fForcedBreak; }
/*
* Adjust text (if any) * * Input: durColumnMax, dupAvailable, lskj and other input parameters * * Output: dupText and dupTail */ if (fEmptyGroupChunk) { dupText = 0; dupTail = 0; dupToAddToNonTextObjects = 0; } else { lserr = AdjustText(lskj, durColumnMax, grchunkext.durTotal - grchunkext.durTrailing, dupAvailable, &(grchunkext.lsgrchnk), &(grchunkext.posichnkBeforeTrailing), lstflow, plsc->lsadjustcontext.fLineCompressed && RightWall.kwall == LineEnd, grchunkext.cNonTextObjectsExpand, fSuppressWiggle, fExact, fForcedBreak, fSuppressTrailingSpaces, &dupText, &dupTail, &dupToAddToNonTextObjects, &cNonTextObjectsToExtend); if (lserr != lserrNone) { plsc->lsstate = LsStateFree; return lserr; }
// Finish justification by expanding non-text object.
if (cNonTextObjectsToExtend != 0 && dupToAddToNonTextObjects > 0) { WidenNonTextObjects(&grchunkext, dupToAddToNonTextObjects, cNonTextObjectsToExtend); } else // We don't compressi and we don't expand the last non-text object on the line
{ dupToAddToNonTextObjects = 0; // don't say we did it
} /*
* Set dup in non-text objects (do CalcPres) in the current GroupChunk * * The job cannot be postponed until after the end of the main loop and done for the whole line * because GetDistanceToDecimalPoint relies on dups in upper level dnodes */ if (!plsline->fAllSimpleText) { // find the last upper level dnode of the groupchunk
if (grchunkext.plsdnNext != NULL) { pdnLast = (grchunkext.plsdnNext)->plsdnPrev; } else { Assert(RightWall.kwall == LineEnd); pdnLast = plsline->lssubl.plsdnLastDisplay; } lserr = CalcPresChunk(plsc, grchunkext.plsdnFirst, pdnLast, CollectGroupChunkPurpose, plsc->lsadjustcontext.fLineCompressed, lskj, (RightWall.kwall == LineEnd)); if (lserr != lserrNone) { plsc->lsstate = LsStateFree; return lserr; } } }
/*
* Set the left wall (if it's a tab - resolve it) * * Input: LeftWall, dupText, dupTail, grchunkext.dupNonTextTotal, grchunkext.durTotal, * dupToAddToNonTextObjects (grchunkext for decimal tab) * * Output: dupWall, durWall */ dupGrpChnk = dupText + grchunkext.dupNonTextTotal + dupToAddToNonTextObjects;
lsktabLast = lsktLeft; // no tab equal left tab for my purpose
if (LeftWall.kwall == Tab) { /* calculate dup of the Left wall now */
if (dupGrpChnk == 0) /* consecutive tabs */
dupWall = LeftWall.upTab - upLeftWall;
else if (LeftWall.lsktab == lsktLeft) dupWall = LeftWall.upTab - upLeftWall; else if (LeftWall.lsktab == lsktRight) dupWall = LeftWall.upTab - upLeftWall - (dupGrpChnk - dupTail); else if (LeftWall.lsktab == lsktCenter) dupWall = LeftWall.upTab - upLeftWall - ((dupGrpChnk - dupTail) / 2); else /* LeftWall.lsktab == lsktDecimal or lsktChar */ { lserr = GetDistanceToTabPoint(&grchunkext, cpLim, LeftWall.lsktab, LeftWall.wchCharTab, LeftWall.pdn->plsdnNext, &dupToTabPoint); if (lserr != lserrNone) { plsc->lsstate = LsStateFree; return lserr; }
dupWall = LeftWall.upTab - upLeftWall - dupToTabPoint; }
// take care of previous text and right margin
if (RightWall.kwall == LineEnd && (upLeftWall + dupWall + dupGrpChnk - dupTail) > upColumnMax) { // We don't want to cross RM because of last center tab
dupWall = upColumnMax - upLeftWall - dupGrpChnk + dupTail; }
if (dupWall < 0) dupWall = 0;
/* LeftWall tab resolving */ LeftWall.pdn->u.real.dup = dupWall; durWall = LeftWall.pdn->u.real.objdim.dur;
// for reproducing Word's bug of forgetting last not-left tab for centering.
lsktabLast = LeftWall.lsktab; dupLastTab = dupWall; } else if (LeftWall.kwall == Pen) { dupWall = LeftWall.pdn->u.pen.dup; /* it've been scaled already */ durWall = LeftWall.pdn->u.pen.dur; } else /* LeftWall.kwall == LineBegin */ { dupWall = plsline->upStartMainText; durWall = plsc->lsadjustcontext.urStartMainText; }
/* update loop variables, move one wall to the right */
upLeftWall += dupWall + dupGrpChnk; urLeftWall += durWall + grchunkext.durTotal; LeftWall = RightWall; } /* end of the main loop */
/*
* prepare output parameters */
plsline->upRightMarginJustify = upColumnMax; plsline->upLimLine = upLeftWall; plsline->upStartTrailing = upLeftWall - dupTail; plsline->upLimUnderline = plsline->upStartTrailing; plsline->lssubl.fDupInvalid = fFalse;
/*
* Do left margin adjustment (not for breakthrough tab) * We're interested in lskalRight and Centered now */ if (lskalign != lskalLeft && !FBreakthroughLine(plsc)) { if (plsc->lsadjustcontext.fForgetLastTabAlignment && lsktabLast != lsktLeft) { // reproduction of an old Word bug: when last tab was not left and was resolved at line end
// they forgot to update their counterpart of upLeftWallForCentering. Word still have to be
// able to show old documents formatted in this crazy way as they were.
upLeftWallForCentering = upLeftWall - dupLastTab - dupTail; } else { upLeftWallForCentering = upLeftWall - dupTail; } if (lskalign == lskalRight) { dupJustifyLine = upColumnMax - upLeftWallForCentering; } else { /* These logic of centering is too simple to be valid, but Word uses it */ dupJustifyLine = (upColumnMax - upLeftWallForCentering) / 2; } // Apply adjustment if hanging punctuation haven't make it negative
if (dupJustifyLine > 0) { plsline->upStartAutonumberingText += dupJustifyLine; plsline->upLimAutonumberingText += dupJustifyLine; plsline->upStartMainText += dupJustifyLine; plsline->upLimLine += dupJustifyLine; plsline->upStartTrailing += dupJustifyLine; plsline->upLimUnderline += dupJustifyLine; } }
if (plsc->lsadjustcontext.fUnderlineTrailSpacesRM && plsline->upLimUnderline < plsline->upRightMarginJustify) { UpdateUpLimUnderline(plsline, dupTail); } if (pdnAutoDecimalTab != NULL) ConvertAutoTabToPen(plsline, pdnAutoDecimalTab);
plsc->lsstate = LsStateFree;
return lserr; }
// %%Function: MatchPresSubline
// %%Contact: victork
//
/*
* Order of operations * * 1. Straighforward scaling down of non-text objects * 2. Adjusting of text by LeftExact * 3. Intelligent rescaling of pens to counteract rounding errors and text non-expansion. * 4. Calling CalcPresentation for all non-text objects */ LSERR MatchPresSubline(PLSSUBL plssubl) { LSERR lserr; const PLSC plsc = plssubl->plsc; LSTFLOW lstflow = plssubl->lstflow; /* text flow of the subline */ const DWORD iobjText = IobjTextFromLsc(&(plsc->lsiobjcontext)); LSDEVRES* pdevres = &(plsc->lsdocinf.lsdevres); LSCP cpLim = plssubl->cpLimDisplay; PLSDNODE pdnFirst = plssubl->plsdnFirst; GRCHUNKEXT grchunkext;
long dupAvailable; /* input for AdjustText */ long dupText, dupTail, dupToAddToNonTextObjects; /* dummy output for AdjustText */ DWORD cNonTextObjectsToExtend; BOOL fDummy1, fDummy2; // dummy parameters
long urAlreadyScaled, upAlreadyScaled, upAlreadyScaledNew;
PLSDNODE pdn; Assert(plssubl->fDupInvalid == fTrue); /* 1. Straighforward scaling down of non-text objects */ ScaleDownLevel(plssubl, &fDummy1, &fDummy2);
/* 2. Adjusting of text on the level by LeftExact */ InitGroupChunkExt(plssubl->plschunkcontext, iobjText, &grchunkext); /* prepare GRCHUNKEXT */
pdn = pdnFirst;
while (pdn != NULL && (pdn->fTab || FIsDnodeNormalPen(pdn))) /* skip GrpChnk Wall(s) */ pdn = pdn->plsdnNext;
while (FDnodeBeforeCpLim(pdn, cpLim)) { lserr = CollectTextGroupChunk(pdn, cpLim, CollectSublinesNone, &grchunkext); if (lserr != lserrNone) return lserr;
/* Adjust text by Left, Exact, no durFreeSpace, ignore any shortcomings */
dupAvailable = UpFromUr(lstflow, pdevres, grchunkext.durTotal) - grchunkext.dupNonTextTotal;
// posichnkBeforeTrailing is undefined when CollectSublinesNone, tell AdjustText about it
grchunkext.posichnkBeforeTrailing.ichnk = grchunkext.lsgrchnk.clsgrchnk; grchunkext.posichnkBeforeTrailing.dcp = 0; lserr = AdjustText(lskjNone, grchunkext.durTotal, grchunkext.durTotal, dupAvailable, &(grchunkext.lsgrchnk), &(grchunkext.posichnkBeforeTrailing), lstflow, fFalse, // compress?
grchunkext.cNonTextObjects, fTrue, // fSuppressWiggle
fTrue, // fExact
fFalse, // fForcedBreak
fFalse, // fSuppressTrailingSpaces
&dupText, &dupTail, &dupToAddToNonTextObjects, &cNonTextObjectsToExtend); if (lserr != lserrNone) return lserr;
pdn = grchunkext.plsdnNext; while (pdn != NULL && (pdn->fTab || FIsDnodeNormalPen(pdn))) /* skip GrpChnk Wall(s) */ pdn = pdn->plsdnNext; }
/* 3-4. Intelligent rescaling of pens to counteract rounding errors and text non-expansion.
* and adjusting of lower levels. Calling CalcPresentation for non-text objects. */
pdn = pdnFirst; urAlreadyScaled = 0; upAlreadyScaled = 0;
while (FDnodeBeforeCpLim(pdn, cpLim)) { if (FIsDnodeReal(pdn)) { urAlreadyScaled += pdn->u.real.objdim.dur; upAlreadyScaled += pdn->u.real.dup; if (IdObjFromDnode(pdn) != iobjText) { // It's always lskjNone and not last object on the line for MatchPresSubline
lserr = (*plsc->lsiobjcontext.rgobj[pdn->u.real.lschp.idObj].lsim.pfnCalcPresentation) (pdn->u.real.pdobj, pdn->u.real.dup, lskjNone, fFalse); if (lserr != lserrNone) return lserr; } } else if (FIsDnodeBorder(pdn)) { // we don't rescale borders to preserve (dupOpeningBorder == dupClosingBorder)
upAlreadyScaled += pdn->u.real.dup; urAlreadyScaled += pdn->u.pen.dur; } else /* pen */ { urAlreadyScaled += pdn->u.pen.dur; upAlreadyScaledNew = UpFromUr(lstflow, pdevres, urAlreadyScaled); pdn->u.pen.dup = upAlreadyScaledNew - upAlreadyScaled; upAlreadyScaled = upAlreadyScaledNew; }
pdn = pdn->plsdnNext; } plssubl->fDupInvalid = fFalse;
return lserrNone; }
// %%Function: AdjustSubline
// %%Contact: victork
//
/*
* * Scale down non-text objects. * Collect GroupChunk * It should cover the whole subline or else do MatchPresSubline instead. * Adjust text by expanding or compressing to given dup. * Call CalcPresentation for all non-text objects. */ LSERR AdjustSubline(PLSSUBL plssubl, LSKJUST lskjust, long dup, BOOL fCompress) { LSERR lserr; const PLSC plsc = plssubl->plsc; LSTFLOW lstflow = plssubl->lstflow; /* text flow of the subline */ const DWORD iobjText = IobjTextFromLsc(&(plsc->lsiobjcontext)); LSDEVRES* pdevres = &(plsc->lsdocinf.lsdevres); LSCP cpLim = plssubl->cpLimDisplay; PLSDNODE pdnFirst = plssubl->plsdnFirst; GRCHUNKEXT grchunkext; COLLECTSUBLINES CollectGroupChunkPurpose;
long dupAvailable, durColumnMax; /* input for AdjustText */ long dupText, dupTail, dupToAddToNonTextObjects; /* dummy output for AdjustText */ DWORD cNonTextObjectsToExtend; BOOL fDummy;
if (plssubl->plsdnFirst == NULL) { return lserrNone; }
Assert(plssubl->fDupInvalid == fTrue); ScaleDownLevel(plssubl, &fDummy, &(plsc->plslineCur->fCollectVisual));
CollectGroupChunkPurpose = (fCompress) ? CollectSublinesForCompression : CollectSublinesForJustification;
InitGroupChunkExt(plssubl->plschunkcontext, iobjText, &grchunkext); /* prepare GRCHUNKEXT */
lserr = CollectTextGroupChunk(pdnFirst, cpLim, CollectGroupChunkPurpose, &grchunkext); if (lserr != lserrNone) return lserr;
if (FDnodeBeforeCpLim(grchunkext.plsdnNext, cpLim)) // more than one GroupChunk -
{ return MatchPresSubline(plssubl); // cancel Expansion
}
dupAvailable = dup - grchunkext.dupNonTextTotal;
if (dupAvailable < 0) // input dup is wrong -
{ return MatchPresSubline(plssubl); // cancel Expansion
}
durColumnMax = UrFromUp(lstflow, pdevres, dup); // get dur by scaling back
lserr = AdjustText(lskjust, durColumnMax, grchunkext.durTotal - grchunkext.durTrailing, dupAvailable, &(grchunkext.lsgrchnk), &(grchunkext.posichnkBeforeTrailing), lstflow, fCompress, // compress?
grchunkext.cNonTextObjects, fTrue, // fSuppressWiggle
fTrue, // fExact
fFalse, // fForcedBreak
fFalse, // fSuppressTrailingSpaces
&dupText, &dupTail, &dupToAddToNonTextObjects, &cNonTextObjectsToExtend); if (lserr != lserrNone) return lserr; if (cNonTextObjectsToExtend != 0 && dupToAddToNonTextObjects > 0) { WidenNonTextObjects(&grchunkext, dupToAddToNonTextObjects, cNonTextObjectsToExtend); }
// fLastOnLine is always false on lower levels
lserr = CalcPresChunk(plsc, plssubl->plsdnFirst, plssubl->plsdnLastDisplay, CollectGroupChunkPurpose, fCompress, lskjust, fFalse);
plssubl->fDupInvalid = fFalse; return lserrNone; }
|