windows-nt/Source/XPSP1/NT/windows/richedit/lssrc/dispmain.c

719 lines
22 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
#include "dispmain.h"
#include "lsc.h"
#include "lsdnode.h"
#include "lstflow.h"
#include "lsline.h"
#include "lssubl.h"
#include "dispul.h"
#include "lstfset.h"
#include "lssubset.h"
#include "dispmisc.h"
#include "dispi.h"
#include "dninfo.h"
#include "memory.h"
#include "port.h"
static LSERR DisplayDnode(PLSC plsc, PLSDNODE pdn, const POINT* pptOrg, POINTUV pt,
UINT kdispmode, LSTFLOW lstflow, const RECT* prectClip,
BOOL fDrawStrike, BOOL fDrawUnderline, long upLimUnderline);
static LSERR ShadeSubline(PLSSUBL plssubl, const POINT* pptOrg, UINT kdispmode,
const RECT* prectClip, long upLimUnderline, long upLeftIndent);
static LSERR DrawBorders(PLSSUBL plssubl, const POINT* pptOrg, UINT kdispmode,
const RECT* prectClip, long upLimUnderline, long upLeftIndent);
static long GetDupUnderline(long up, long dup, long upLimUnderline);
static BOOL FGetNeighboringOpeningBorder(PLSDNODE pdnClosingBorder, PLSDNODE pdnNext, POINTUV* pptStart,
LSCP cpLim, LSTFLOW lstflowMain,
PLSDNODE* ppdnOpeningBorder, POINTUV* pptStartOpeningBorder);
#define FIsDnodeToShade(pdn, cpLim) (FDnodeBeforeCpLim(pdn, cpLim) && FIsDnodeReal(pdn) && \
!(pdn)->u.real.lschp.fInvisible && (pdn)->u.real.lschp.fShade)
#define UpdateMaximum(a,b) if ((a) < (b)) (a) = (b); else
#define FIsDnodeOpeningBorder(pdn, lstflowMain) ((pdn)->fOpenBorder ^ ((pdn)->plssubl->lstflow != (lstflowMain)))
#define FIsDnodeClosingBorder(pdn, lstflowMain) (!FIsDnodeOpeningBorder(pdn, lstflowMain))
// %%Function: DisplaySublineCore
// %%Contact: victork
//
//
// Displays subline with shading, striking and underlining, merging consecutive underlined dnodes
//
// Logic to select underlining method:
//
// If (metrics_are_good)
// Draw_by_fnDrawUnderline;
// else
// if (There_is_merging_going)
// Draw_by_pfnDrawUnderlineAsText;
// else
// Draw_along_with_Display;
LSERR DisplaySublineCore(
PLSSUBL plssubl, /* subline to display */
const POINT* pptOrg, /* (x,y) starting point */
UINT kdispmode, /* transparent or opaque */
const RECT* prectClip, /* clipping rect (x,y) */
long upLimUnderline,
long upLeftIndent)
{
LSERR lserr;
LSCP cpLim = plssubl->cpLimDisplay;
PLSC plsc = plssubl->plsc;
LSTFLOW lstflowMain = plssubl->lstflow;
BOOL fCollectVisual = plsc->plslineDisplay->fCollectVisual;
LSSTRIKEMETRIC lsstrikemetric;
// Underline merge group - normal scenario: we first count dnodes willing to participate
// in merging looking ahead, then draw them, then underline them as a whole
PLSDNODE pdnFirstInGroup = NULL; /* First dnode in Underline merge group */
int cdnodesLeftInGroup = 0; /* these are not displayed yet */
int cdnodesToUnderline = 0; /* these are already displayed */
BOOL fGoodUnderline = fFalse; /* is there metric for UL */
LSULMETRIC lsulmetric; /* merge metric info (if fGoodUnderline) */
long upUnderlineStart = 0; /* Starting point for the group */
BOOL fMergeUnderline = fFalse; /* There is more then one dnode in the group */
BOOL fUnderlineWithDisplay, fStrikeWithDisplay;
POINTUV pt;
PLSDNODE pdn;
FLineValid(plsc->plslineDisplay, plsc); // Assert the display context is valid
Assert(plssubl->plsdnUpTemp == NULL); // against displaying accepted sublines
if (fCollectVisual)
{
CreateDisplayTree(plssubl);
}
if (plsc->plslineDisplay->AggregatedDisplayFlags & fPortDisplayShade)
{
lserr = ShadeSubline(plssubl, pptOrg, kdispmode, prectClip, upLimUnderline, upLeftIndent);
if (lserr != lserrNone) return lserr;
}
pt.u = upLeftIndent;
pt.v = 0;
pdn = AdvanceToFirstDnode(plssubl, lstflowMain, &pt);
while (FDnodeBeforeCpLim(pdn, cpLim))
{
if (pdn->klsdn == klsdnReal && !pdn->u.real.lschp.fInvisible)
{
/* Real dnode */
fStrikeWithDisplay = fFalse;
if (pdn->u.real.lschp.fStrike)
{
BOOL fGoodStrike;
lserr = GetStrikeMetric(plsc, pdn, lstflowMain, &lsstrikemetric, &fGoodStrike);
if (lserr != lserrNone) return lserr;
fStrikeWithDisplay = !fGoodStrike;
}
fUnderlineWithDisplay = fFalse;
if (pdn->u.real.lschp.fUnderline && pt.u < upLimUnderline)
{
/* Node has underline */
if (cdnodesLeftInGroup == 0)
{
/* There are no on-going UL group */
/* Find out how many dnodes will participate in the merge and what metric to use */
lserr = GetUnderlineMergeMetric(plsc, pdn, pt, upLimUnderline, lstflowMain,
cpLim, &lsulmetric, &cdnodesLeftInGroup , &fGoodUnderline);
if (lserr != lserrNone) return lserr;
fMergeUnderline = (cdnodesLeftInGroup > 1);
cdnodesToUnderline = 0;
}
if (!fGoodUnderline)
fUnderlineWithDisplay = !fMergeUnderline;
else
fUnderlineWithDisplay = fFalse;
if (!fUnderlineWithDisplay)
{
if (cdnodesToUnderline == 0)
{
/* Mark starting point of underline merge */
pdnFirstInGroup = pdn;
upUnderlineStart = pt.u;
}
/* Add to pending UL dnode count */
++cdnodesToUnderline;
}
// current dnode will be displayed shortly - consider it done
--cdnodesLeftInGroup ;
}
lserr = DisplayDnode(plsc, pdn, pptOrg, pt, kdispmode, lstflowMain, prectClip,
fStrikeWithDisplay, fUnderlineWithDisplay, upLimUnderline);
if (lserr != lserrNone) return lserr;
if (pdn->u.real.lschp.fStrike && !fStrikeWithDisplay)
{
lserr = StrikeDnode(plsc, pdn, pptOrg, pt, &lsstrikemetric, kdispmode, prectClip,
upLimUnderline, lstflowMain);
if (lserr != lserrNone) return lserr;
}
/* Draw any pending UL after last dnode in group has been drawn */
if (cdnodesToUnderline != 0 && cdnodesLeftInGroup == 0)
{
lserr = DrawUnderlineMerge(plsc, pdnFirstInGroup, pptOrg,
cdnodesToUnderline, upUnderlineStart, fGoodUnderline, &lsulmetric,
kdispmode, prectClip, upLimUnderline, lstflowMain);
if (lserr != lserrNone) return lserr;
cdnodesToUnderline = 0;
}
}
pdn = AdvanceToNextDnode(pdn, lstflowMain, &pt);
}
if (fCollectVisual)
{
// call display method for submitting dnodes
pt.u = upLeftIndent;
pt.v = 0;
pdn = AdvanceToFirstSubmittingDnode(plssubl, lstflowMain, &pt);
while (FDnodeBeforeCpLim(pdn, cpLim))
{
lserr = DisplayDnode(plsc, pdn, pptOrg, pt, kdispmode, lstflowMain, prectClip,
fFalse, fFalse, upLimUnderline);
if (lserr != lserrNone) return lserr;
pdn = AdvanceToNextSubmittingDnode(pdn, lstflowMain, &pt);
}
}
if (plsc->plslineDisplay->AggregatedDisplayFlags & fPortDisplayBorder)
{
lserr = DrawBorders(plssubl, pptOrg, kdispmode, prectClip, upLimUnderline, upLeftIndent);
if (lserr != lserrNone) return lserr;
}
if (fCollectVisual)
{
// destroy display tree
DestroyDisplayTree(plssubl);
}
return lserrNone;
}
// %%Function: DisplayDnode
// %%Contact: victork
static LSERR DisplayDnode(PLSC plsc, PLSDNODE pdn, const POINT* pptOrg, POINTUV pt,
UINT kdispmode, LSTFLOW lstflowMain, const RECT* prectClip,
BOOL fDrawStrike, BOOL fDrawUnderline, long upLimUnderline)
{
PDOBJ pdobj;
DISPIN dispin;
pdobj = pdn->u.real.pdobj;
dispin.plschp = &(pdn->u.real.lschp);
dispin.plsrun = pdn->u.real.plsrun;
dispin.kDispMode = kdispmode;
dispin.lstflow = pdn->plssubl->lstflow;
dispin.prcClip = (RECT*) prectClip;
dispin.fDrawUnderline = fDrawUnderline;
dispin.fDrawStrikethrough = fDrawStrike;
dispin.heightsPres = pdn->u.real.objdim.heightsPres;
dispin.dup = pdn->u.real.dup;
dispin.dupLimUnderline = GetDupUnderline(pt.u, dispin.dup, upLimUnderline);
if (dispin.lstflow != lstflowMain)
{
// Dnode lstflow is opposite to lstflowMain - get real starting point
pt.u = pt.u + dispin.dup - 1;
// Partial underlining can only happen on the top level
Assert(dispin.dupLimUnderline == 0 || dispin.dupLimUnderline == dispin.dup);
}
pt.v += pdn->u.real.lschp.dvpPos;
LsPointXYFromPointUV(pptOrg, lstflowMain, &pt, &(dispin.ptPen));
return (*plsc->lsiobjcontext.rgobj[pdn->u.real.lschp.idObj].lsim.pfnDisplay)(pdobj, &dispin);
}
// %%Function: ShadeSubline
// %%Contact: victork
LSERR ShadeSubline(PLSSUBL plssubl, /* subline to shade */
const POINT* pptOrg, /* (x,y) starting point */
UINT kdispmode, /* transparent or opaque */
const RECT* prectClip, /* clipping rect (x,y) */
long upLimUnderline,
long upLeftIndent)
{
LSERR lserr;
LSCP cpLim = plssubl->cpLimDisplay;
PLSC plsc = plssubl->plsc;
PLSLINE plsline = plsc->plslineDisplay;
LSTFLOW lstflowMain = plssubl->lstflow;
POINTUV pt;
PLSDNODE pdn;
HEIGHTS heightsLineWithAddedSpace;
HEIGHTS heightsLineWithoutAddedSpace;
OBJDIM objdimSubline;
POINT ptStart;
PLSRUN plsrunFirst, plsrunPrevious;
long upStart;
long dupInclTrail, dupExclTrail;
HEIGHTS heightsRunsInclTrail;
HEIGHTS heightsRunsExclTrail;
BOOL fInterruptShading;
BOOL fCollectVisual = plsc->plslineDisplay->fCollectVisual;
heightsLineWithAddedSpace.dvAscent = plsline->dvpAbove + plsline->lslinfo.dvpAscent;
heightsLineWithAddedSpace.dvDescent = plsline->dvpBelow + plsline->lslinfo.dvpDescent;
heightsLineWithAddedSpace.dvMultiLineHeight = dvHeightIgnore;
heightsLineWithoutAddedSpace.dvAscent = plsline->lslinfo.dvpAscent;
heightsLineWithoutAddedSpace.dvDescent = plsline->lslinfo.dvpDescent;
heightsLineWithoutAddedSpace.dvMultiLineHeight = dvHeightIgnore;
lserr = LssbGetObjDimSubline(plssubl, &lstflowMain, &objdimSubline);
if (lserr != lserrNone) return lserr;
if (fCollectVisual)
{
// shade submitting dnodes - pretend they are on the top level, no merging for them
pt.u = upLeftIndent;
pt.v = 0;
pdn = AdvanceToFirstSubmittingDnode(plssubl, lstflowMain, &pt);
while (FDnodeBeforeCpLim(pdn, cpLim))
{
if (FIsDnodeToShade(pdn, cpLim))
{
LsPointXYFromPointUV(pptOrg, lstflowMain, &pt, &ptStart);
dupInclTrail = pdn->u.real.dup;
dupExclTrail = GetDupUnderline(pt.u, dupInclTrail, upLimUnderline);
lserr = (*plsc->lscbk.pfnShadeRectangle)(plsc->pols, pdn->u.real.plsrun, &ptStart,
&heightsLineWithAddedSpace, &heightsLineWithoutAddedSpace,
&(objdimSubline.heightsPres),
&(pdn->u.real.objdim.heightsPres), &(pdn->u.real.objdim.heightsPres),
dupExclTrail, dupInclTrail,
lstflowMain, kdispmode, prectClip);
if (lserr != lserrNone) return lserr;
}
pdn = AdvanceToNextSubmittingDnode(pdn, lstflowMain, &pt);
}
}
pt.u = upLeftIndent;
pt.v = 0;
pdn = AdvanceToFirstDnode(plssubl, lstflowMain, &pt);
while (FDnodeBeforeCpLim(pdn, cpLim) && !FIsDnodeToShade(pdn, cpLim))
{
pdn = AdvanceToNextDnode(pdn, lstflowMain, &pt);
}
// next loop will do one shading merge at a run
while (FDnodeBeforeCpLim(pdn, cpLim))
{
// pdn is the first dnode to participate in the shade merge
// initialize the merge with this dnode data
LsPointXYFromPointUV(pptOrg, lstflowMain, &pt, &ptStart);
plsrunFirst = pdn->u.real.plsrun;
upStart = pt.u;
heightsRunsInclTrail = pdn->u.real.objdim.heightsPres;
// What should we have in heightsRunsExclTrail if all shading is in trailing spaces?
// I decided to put heights of the first run there for convenience sake
// Client can check for dupExclTrail == 0.
heightsRunsExclTrail = heightsRunsInclTrail;
// We will now append to the merge as many dnodes as possible
// The loop will stop when dnode doesn't need to be shaded - while condition
// or if callback says two dnodes are not to be shaded together - break inside
plsrunPrevious = pdn->u.real.plsrun;
pdn = AdvanceToNextDnode(pdn, lstflowMain, &pt);
while (FIsDnodeToShade(pdn, cpLim))
{
lserr = (*plsc->lscbk.pfnFInterruptShade)(plsc->pols, plsrunPrevious,pdn->u.real.plsrun,
&fInterruptShading);
if (lserr != lserrNone) return lserr;
if (fInterruptShading)
{
break;
}
plsrunPrevious = pdn->u.real.plsrun;
UpdateMaximum(heightsRunsInclTrail.dvAscent, pdn->u.real.objdim.heightsPres.dvAscent);
UpdateMaximum(heightsRunsInclTrail.dvDescent, pdn->u.real.objdim.heightsPres.dvDescent);
UpdateMaximum(heightsRunsInclTrail.dvMultiLineHeight, pdn->u.real.objdim.heightsPres.dvMultiLineHeight);
if (pt.u < upLimUnderline)
{
UpdateMaximum(heightsRunsExclTrail.dvAscent, pdn->u.real.objdim.heightsPres.dvAscent);
UpdateMaximum(heightsRunsExclTrail.dvDescent, pdn->u.real.objdim.heightsPres.dvDescent);
UpdateMaximum(heightsRunsExclTrail.dvMultiLineHeight, pdn->u.real.objdim.heightsPres.dvMultiLineHeight);
}
pdn = AdvanceToNextDnode(pdn, lstflowMain, &pt);
}
// Merge is stopped - time to draw
dupInclTrail = pt.u - upStart;
dupExclTrail = GetDupUnderline(upStart, dupInclTrail, upLimUnderline);
lserr = (*plsc->lscbk.pfnShadeRectangle)(plsc->pols, plsrunFirst, &ptStart,
&heightsLineWithAddedSpace, &heightsLineWithoutAddedSpace,
&(objdimSubline.heightsPres),
&heightsRunsExclTrail, &heightsRunsInclTrail,
dupExclTrail, dupInclTrail,
lstflowMain, kdispmode, prectClip);
if (lserr != lserrNone) return lserr;
// get to the beginning of the next shade merge
while (FDnodeBeforeCpLim(pdn, cpLim) && !FIsDnodeToShade(pdn, cpLim))
{
pdn = AdvanceToNextDnode(pdn, lstflowMain, &pt);
}
}
return lserrNone;
}
// %%Function: GetDupUnderline
// %%Contact: victork
//
// Calculate dup of underlined part (of dnode). Deals with situations when upLimUnderline is
// outside of [upStart, upStart + dup]
static long GetDupUnderline(long upStart, long dup, long upLimUnderline)
{
long dupLimUnderline;
dupLimUnderline = upLimUnderline - upStart;
if (dupLimUnderline >= dup)
{
dupLimUnderline = dup;
}
else if (dupLimUnderline < 0)
{
dupLimUnderline = 0;
}
return dupLimUnderline;
}
// %%Function: DrawBorders
// %%Contact: victork
LSERR DrawBorders(PLSSUBL plssubl,
const POINT* pptOrg, /* (x,y) starting point */
UINT kdispmode, /* transparent or opaque */
const RECT* prectClip, /* clipping rect (x,y) */
long upLimUnderline,
long upLeftIndent)
{
LSERR lserr;
LSCP cpLim = plssubl->cpLimDisplay;
PLSC plsc = plssubl->plsc;
PLSLINE plsline = plsc->plslineDisplay;
LSTFLOW lstflowMain = plssubl->lstflow;
HEIGHTS heightsLineWithAddedSpace;
HEIGHTS heightsLineWithoutAddedSpace;
OBJDIM objdimSubline;
HEIGHTS heightsRuns;
long upStart, dupBorder, dupBordered;
POINT ptStart;
PLSRUN plsrunOpeningBorder, plsrunClosingBorder;
POINTUV pt, ptAfterClosingBorder;
PLSDNODE pdn, pdnPrev, pdnClosingBorder, pdnAfterClosingBorder;
BOOL fClosingOpeningBorderSequenceFound;
PLSDNODE pdnNextOpeningBorder;
POINTUV ptStartNextOpeningBorder;
BOOL fInterruptBorder;
heightsLineWithAddedSpace.dvAscent = plsline->dvpAbove + plsline->lslinfo.dvpAscent;
heightsLineWithAddedSpace.dvDescent = plsline->dvpBelow + plsline->lslinfo.dvpDescent;
heightsLineWithAddedSpace.dvMultiLineHeight = dvHeightIgnore;
heightsLineWithoutAddedSpace.dvAscent = plsline->lslinfo.dvpAscent;
heightsLineWithoutAddedSpace.dvDescent = plsline->lslinfo.dvpDescent;
heightsLineWithoutAddedSpace.dvMultiLineHeight = dvHeightIgnore;
lserr = LssbGetObjDimSubline(plssubl, &lstflowMain, &objdimSubline);
if (lserr != lserrNone) return lserr;
pt.u = upLeftIndent;
pt.v = 0;
pdn = AdvanceToFirstDnode(plssubl, lstflowMain, &pt);
// next loop will draw one border at a run
while (FDnodeBeforeCpLim(pdn, cpLim))
{
// first find an opening border
while (FDnodeBeforeCpLim(pdn, cpLim) && !FIsDnodeBorder(pdn))
{
pdn = AdvanceToNextDnode(pdn, lstflowMain, &pt);
}
if (FDnodeBeforeCpLim(pdn, cpLim))
{
// border is found - it must be an opening one
Assert(FIsDnodeOpeningBorder(pdn, lstflowMain));
// remember the starting point and border width
upStart = pt.u;
LsPointXYFromPointUV(pptOrg, lstflowMain, &pt, &ptStart);
dupBorder = pdn->u.pen.dup;
// take lsrun from the first bordered run
pdn = AdvanceToNextDnode(pdn, lstflowMain, &pt);
Assert(FDnodeBeforeCpLim(pdn, cpLim) && FIsDnodeReal(pdn));
plsrunOpeningBorder = pdn->u.real.plsrun;
// start collecting max run height
heightsRuns = pdn->u.real.objdim.heightsPres;
// now look for an closing border to draw, collecting max run height
// loop will be ended by break
for (;;)
{
// find a border
pdnPrev = NULL;
while (FDnodeBeforeCpLim(pdn, cpLim) && !FIsDnodeBorder(pdn))
{
if (FIsDnodeReal(pdn))
{
UpdateMaximum(heightsRuns.dvAscent, pdn->u.real.objdim.heightsPres.dvAscent);
UpdateMaximum(heightsRuns.dvDescent, pdn->u.real.objdim.heightsPres.dvDescent);
UpdateMaximum(heightsRuns.dvMultiLineHeight, pdn->u.real.objdim.heightsPres.dvMultiLineHeight);
}
pdnPrev = pdn;
pdn = AdvanceToNextDnode(pdn, lstflowMain, &pt);
}
Assert(FDnodeBeforeCpLim(pdn, cpLim));
// border is found - it must be a closing one
// Sequence opening border - closing border is prohibited by formatting
Assert(pdnPrev != NULL);
Assert(FIsDnodeReal(pdnPrev));
Assert(FIsDnodeClosingBorder(pdn, lstflowMain));
Assert(pdn->u.pen.dup == dupBorder);
pdnClosingBorder = pdn;
plsrunClosingBorder = pdnPrev->u.real.plsrun;
pdn = AdvanceToNextDnode(pdn, lstflowMain, &pt);
ptAfterClosingBorder = pt;
pdnAfterClosingBorder = pdn;
// check for the "surplus borders" situation: closing border and opening border of the same
// type brought together by submitting sublines. (Hard to check at formatting time)
// It can be more complicated if there are bordered trailing spaces between the two borders
// (Trailing spaces can happen in the middle of the line in Bidi case). The problem is
// that border is moved away from trailing spaces at SetBreak time. We try to restore
// bordering of trailing spaces when they are in the middle of bordered line below.
fClosingOpeningBorderSequenceFound = FGetNeighboringOpeningBorder(pdnClosingBorder, pdn, &pt,
cpLim, lstflowMain, &pdnNextOpeningBorder, &ptStartNextOpeningBorder);
if (fClosingOpeningBorderSequenceFound)
{
pdn = pdnNextOpeningBorder;
pt = ptStartNextOpeningBorder;
pdn = AdvanceToNextDnode(pdn, lstflowMain, &pt);
Assert(FDnodeBeforeCpLim(pdn, cpLim) && FIsDnodeReal(pdn));
lserr = (*plsc->lscbk.pfnFInterruptBorder)(plsc->pols, plsrunClosingBorder,
pdn->u.real.plsrun, &fInterruptBorder);
if (lserr != lserrNone) return lserr;
if (!fInterruptBorder)
{
// Client decided against interrupting border here. These two border dnodes
// will be ignored. Space reserved for them by formatting will be left empty.
// Continue seeking for closing border starting from pdn
continue;
}
}
// No special situation - we are ready to display
// Well, we are almost ready. Word doesn't normally draw borders in trailing spaces,
// just reserve space for them and leave this space blank. In FE Word, however,
// borders are drawn if underlining of trailing spaces is required.
// We hack in the following way: Borders in trailing area are deleted after formatting.
// If there are a border which opens in text and closes in trailing spaces, it is moved
// to the left to exclude trailing spaces. If the fUnderlineTrailSpacesRM flag is on
// the "moved" border is marked and now have to be displayed up to upLimUnderline.
// Yes, it's bad, it will appear painted over already displayed spaces (queries!) and
// what about a scenario when not all trailing spaces are bordered? We know, we know.
// Word can even get a "negative" border with a negative advance field.
dupBordered = ptAfterClosingBorder.u - upStart;
if (pdnClosingBorder->fBorderMovedFromTrailingArea)
{
Assert(ptAfterClosingBorder.u <= upLimUnderline);
dupBordered = upLimUnderline - upStart;
}
lserr = (*plsc->lscbk.pfnDrawBorder)(plsc->pols, plsrunOpeningBorder, &ptStart,
&heightsLineWithAddedSpace, &heightsLineWithoutAddedSpace,
&(objdimSubline.heightsPres), &heightsRuns,
dupBorder, dupBordered,
lstflowMain, kdispmode, prectClip);
if (lserr != lserrNone) return lserr;
// maybe we peeped ahead checking for the surplus borders - return
pdn = pdnAfterClosingBorder;
pt = ptAfterClosingBorder;
break;
}
}
// Previous border is drawn, start looking for the next one from pdn.
}
return lserrNone;
}
// Find an opening border neighboring pdnClosingBorder broght together by submitting sublines.
// Ignore trailing spaces that lost their borders during SetBreak - their heights will be ignored.
// Input: pdnClosingBorder
// pdnNext - next to pdnClosingBorder (in visual order)
// ptStart - starting poing of pdnNext (in visual order)
// cpLim and lstflowMain
// Output: pdnOpeningBorder and ptStartOpeningBorder
static BOOL FGetNeighboringOpeningBorder(PLSDNODE pdnClosingBorder, PLSDNODE pdnNext, POINTUV* pptStart,
LSCP cpLim, LSTFLOW lstflowMain,
PLSDNODE* ppdnOpeningBorder, POINTUV* pptStartOpeningBorder)
{
PLSDNODE pdn;
POINTUV pt;
pdn = pdnNext;
pt = *pptStart;
// skip spaces that were bordered once
// not sure about spaces, but what else could be skipped?
while (FDnodeBeforeCpLim(pdn, cpLim) && FIsDnodeReal(pdn) && pdn->u.real.lschp.fBorder)
{
pdn = AdvanceToNextDnode(pdn, lstflowMain, &pt);
}
if (!FDnodeBeforeCpLim(pdn, cpLim))
{
return fFalse;
}
// looking for an opening border from another subline
if (FIsDnodeOpeningBorder(pdn, lstflowMain) && pdn->plssubl != pdnClosingBorder->plssubl)
{
*ppdnOpeningBorder = pdn;
*pptStartOpeningBorder = pt;
return fTrue;
}
return fFalse;
}
// N.B.
// Interruption of underlining/shading logic by invisible dnode is OK, because we are sure that no
// underlining/shading is allowed in preprinted forms.