WindowsXP-SP1/windows/richedit/lssrc/lscrline.c
2020-09-30 16:53:49 +02:00

1940 lines
60 KiB
C

#include <limits.h>
#include "lsmem.h" /* memset() */
#include "break.h"
#include "dnutils.h"
#include "iobj.h"
#include "iobjln.h"
#include "lsc.h"
#include "lschp.h"
#include "lscrline.h"
#include "lsdevres.h"
#include "lskysr.h"
#include "lsffi.h"
#include "lsidefs.h"
#include "lsline.h"
#include "lsfetch.h"
#include "lstext.h"
#include "prepdisp.h"
#include "tlpr.h"
#include "qheap.h"
#include "sublutil.h"
#include "zqfromza.h"
#include "lscfmtfl.h"
#include "limqmem.h"
#include "ntiman.h"
typedef struct
{
long urLeft;
BOOL fAutoDecimalTab;
long durAutoDecimalTab;
LSCP cpFirstVis;
BOOL fAutonumber;
BOOL fStopped;
BOOL fYsrChangeAfter;
WCHAR wchYsr; /* we need memory to keep wchYsr for kysrChangeAfter */
} LINEGEOMETRY;
static LSERR CreateLineCore(PLSC, /* IN: ptr to line services context */
LSCP, /* IN: starting cp in line */
long, /* IN: column width in twips */
const BREAKREC*, /* IN: previous line's break records */
DWORD, /* IN: number of previous line's break records */
DWORD, /* IN: size of the array of current line's break records */
BREAKREC*, /* OUT: current line's break records */
DWORD*, /* OUT: actual number of current line's break records */
LSLINFO*, /* OUT: visible line info to fill in */
PLSLINE*, /* OUT: ptr to line opaque to client */
BOOL*); /* OUT fSuccessful: false means insufficient fetch */
static BOOL FRoundingOK(void);
static LSERR CannotCreateLine(PLSLINE*, /* IN: ponter to a line structure to be deleted */
LSERR); /* IN: code of an error */
static LSERR ErrReleasePreFetchedRun (PLSC, /* IN: ptr to line services context */
PLSRUN, /* IN: ponter to a run structure to be deleted */
LSERR); /* IN: code of an error */
static LSERR EndFormatting(PLSC, /* IN: ptr to line services context */
enum endres, /* IN: type of line ending to put in lslinfo */
LSCP, /* IN: cpLim to put in lslinfo */
LSDCP, /* IN: dcpDepend to put in lslinfo*/
LSLINFO*); /* OUT: lslinfo to fill in, output of LsCreateLine*/
static LSERR FiniFormatGeneralCase (
PLSC, /* IN: ptr to line services context */
const BREAKREC*,/* IN: input array of break records */
DWORD, /* IN: number of records in input array */
DWORD, /* IN: size of the output array */
BREAKREC*, /* OUT: output array of break records */
DWORD*, /* OUT:actual number of records in array*/
LSLINFO*, /* OUT: lslinfo to fill in, output of LsCreateLine*/
BOOL*); /* OUT fSuccessful: false means insufficient fetch */
static LSERR FiniEndLine(PLSC, /* IN: ptr to line services context */
ENDRES, /* IN: how the line ended */
LSCP /* IN: cpLim of a line as a result of breaking,
can be changed in this procedure*/,
LSDCP, /* IN: dcpDepend (amount of characters after breaking point
that has participated in breaking decision)
can be changed in this procedure */
LSLINFO*); /* OUT: lslinfo to fill in, output of LsCreateLine*/
static LSERR FetchUntilVisible(
PLSC, /* IN: ptr to line services context */
LSPAP*, /* IN/OUT current lspap before and after */
LSCP*, /* IN/OUT current cp before and after */
LSFRUN*, /* IN/OUT current lsfrun before and after */
PLSCHP, /* IN/OUT current lschp before and after */
BOOL*, /* OUT fStopped: procedure stopped fetching because has not been allowed
to go across paragraph boundaries (result CheckPara Boundaries) */
BOOL*); /* OUT fNewPara: procedure crossed paragraph boundaries */
static LSERR InitTextParams(PLSC, /* IN: ptr to line services context */
LSCP, /* IN: cp to start fetch */
long, /* IN: duaColumn */
LSFRUN*, /* OUT: lsfrun of the first run */
PLSCHP, /* OUT: lsfrun of the first run */
LINEGEOMETRY*); /* OUT: set of flags and parameters about a line */
static LSERR FiniAuto(PLSC , /* IN: ptr to line services context */
BOOL , /* IN: fAutonumber */
BOOL , /* IN: fAutoDecimalTab */
PLSFRUN , /* IN: first run of the main text */
long, /* IN: durAutoDecimalTab */
const BREAKREC*, /* IN: input array of break records */
DWORD, /* IN: number of records in input array */
DWORD, /* IN: size of the output array */
BREAKREC*, /* OUT: output array of break records */
DWORD*, /* OUT:actual number of records in array*/
LSLINFO*, /* OUT: lslinfo to fill in, output of LsCreateLine*/
BOOL*); /* OUT fSuccessful: false means insufficient fetch */
static LSERR InitCurLine(PLSC plsc, /* IN: ptr to line services context */
LSCP cpFirst); /* IN: first cp in al line */
static LSERR RemoveLineObjects(PLSLINE plsline); /* IN: ponter to a line structure */
static LSERR GetYsrChangeAfterRun(
PLSC plsc, /* IN: ptr to line services context */
LSCP cp, /* IN: cp to start fetch */
BOOL* pfYsrChangeAfter, /* OUT: is it hyphenation of the previous line */
PLSFRUN plsfrun, /* OUT: lsfrun of modified first run */
PLSCHP plschp, /* OUT: lschp of modified first run */
LINEGEOMETRY*); /* OUT: to put wchYsr */
static LSERR FillTextParams(
PLSC plsc, /* IN: ptr to line services context */
LSCP cp, /* IN: cp to start fetch */
long duaCol, /* IN: duaColumn */
PLSPAP plspap, /* IN: paragraph properties */
BOOL fFirstLineInPara, /* IN: flag fFirstLineInPara */
BOOL fStopped, /* IN: flag fStopped */
LINEGEOMETRY*); /* OUT: set of flags and parameters about a line */
static LSERR FiniChangeAfter(
PLSC plsc, /* IN: ptr to line services context */
LSFRUN* plsfrun, /* IN: lsfrun of modified first run */
const BREAKREC*, /* IN: input array of break records */
DWORD, /* IN: number of records in input array */
DWORD, /* IN: size of the output array */
BREAKREC*, /* OUT: output array of break records */
DWORD*, /* OUT:actual number of records in array*/
LSLINFO*, /* OUT: lslinfo to fill in, output of LsCreateLine*/
BOOL*); /* OUT fSuccessful: false means insufficient fetch */
/* L I M R G */
/*----------------------------------------------------------------------------
%%Function: LimRg
%%Contact: lenoxb
Returns # of elements in an array.
----------------------------------------------------------------------------*/
#define LimRg(rg) (sizeof(rg)/sizeof((rg)[0]))
#define fFmiAdvancedFormatting (fFmiPunctStartLine | fFmiHangingPunct)
#define FBreakJustSimple(lsbrj) (lsbrj == lsbrjBreakJustify || lsbrj == lsbrjBreakThenSqueeze)
#define FAdvancedTypographyEnabled(plsc, cbreakrec) \
(FNominalToIdealBecauseOfParagraphProperties(plsc->grpfManager, \
plsc->lsadjustcontext.lskj) || \
!FBreakJustSimple((plsc)->lsadjustcontext.lsbrj) ||\
cbreakrec != 0 \
)
#define fFmiSpecialSpaceBreaking (fFmiWrapTrailingSpaces | fFmiWrapAllSpaces)
#define fFmiQuickBreakProhibited (fFmiSpecialSpaceBreaking | fFmiDoHyphenation)
/* F T R Y Q U I C K B R E A K */
/*----------------------------------------------------------------------------
%%Macro: FTryQuickBreak
%%Contact: igorzv
"Returns" fTrue when the formatter flags indicate that it it may be
possible to use QuickBreakText() instead of the more expensive
BreakGeneralCase().
----------------------------------------------------------------------------*/
#define FTryQuickBreak(plsc) ((((plsc)->grpfManager & fFmiQuickBreakProhibited) == 0) && \
((plsc)->lMarginIncreaseCoefficient == LONG_MIN) \
)
#define GetMainSubline(plsc) \
(Assert(FWorkWithCurrentLine(plsc)),\
&((plsc)->plslineCur->lssubl))
#define FPapInconsistent(plspap) \
((((plspap)->lsbrj == lsbrjBreakJustify || \
(plspap)->lsbrj == lsbrjBreakWithCompJustify) \
&& (plspap)->uaRightBreak < uLsInfiniteRM \
&& (plspap)->uaRightBreak != (plspap)->uaRightJustify) \
|| ((plspap)->lsbrj == lsbrjBreakThenExpand \
&& (plspap)->uaRightBreak < (plspap)->uaRightJustify) \
|| ((plspap)->lsbrj == lsbrjBreakThenSqueeze \
&& (plspap)->uaRightBreak > (plspap)->uaRightJustify) \
|| ((plspap)->lsbrj != lsbrjBreakWithCompJustify \
&& (plspap)->grpf & fFmiHangingPunct) \
|| ((plspap)->lsbrj == lsbrjBreakWithCompJustify \
&& (plspap)->lskj == lskjFullGlyphs))
/* ---------------------------------------------------------------------- */
/* L S C R E A T E L I N E */
/*----------------------------------------------------------------------------
%%Function: LsCreateLine
%%Contact: igorzv
Parameters:
plsc - (IN) ptr to line services context
cpFirst - (IN) starting cp in line
duaColumn - (IN) column width in twips
pbreakrecPrev - (IN) previous line's break records
breakrecMacPrev - (IN) number of previous line's break records
breakrecMaxCurrent- (IN) size of the array of current line's break records
pbreakrecCurrent- (OUT) current line's break records
pbreakrecMacCurrent-(OUT) actual number of current line's break records
plsinfo - (OUT) visible line info to fill in
pplsline - (OUT) ptr to line opaque to client
An exported LineServices API.
----------------------------------------------------------------------------*/
LSERR WINAPI LsCreateLine(PLSC plsc,
LSCP cpFirst,
long duaColumn,
const BREAKREC* pbreakrecPrev,
DWORD breakrecMacPrev,
DWORD breakrecMaxCurrent,
BREAKREC* pbreakrecCurrent,
DWORD* pbreakrecMacCurrent,
LSLINFO* plslinfo,
PLSLINE* pplsline)
{
LSERR lserr;
BOOL fSuccessful;
/* Check parameters and enviroment */
Assert(FRoundingOK());
if (plslinfo == NULL || pplsline == NULL || pbreakrecMacCurrent == NULL)
return lserrNullOutputParameter;
*pplsline = NULL;
*pbreakrecMacCurrent = 0; /* it's very important to initialize number of break records
because for example quick break doesn't work with break records */
if (!FIsLSC(plsc))
return lserrInvalidContext;
if (plsc->lsstate != LsStateFree)
return lserrContextInUse;
Assert(FIsLsContextValid(plsc));
if (pbreakrecPrev == NULL && breakrecMacPrev != 0)
return lserrInvalidParameter;
if (pbreakrecCurrent == NULL && breakrecMaxCurrent != 0)
return lserrInvalidParameter;
if (duaColumn < 0)
return lserrInvalidParameter;
if (duaColumn > uLsInfiniteRM)
duaColumn = uLsInfiniteRM;
/* if we have current line we must prepare it for display before creating of new line */
/* can change context. We've delayed this untill last moment because of optimisation reasons */
if (plsc->plslineCur != NULL)
{
lserr = PrepareLineForDisplayProc(plsc->plslineCur);
if (lserr != lserrNone)
return lserr;
plsc->plslineCur = NULL;
}
plsc->lMarginIncreaseCoefficient = LONG_MIN;
do /* loop allowing change of exceeded right margin if it's not sufficient */
{
lserr = CreateLineCore(plsc, cpFirst, duaColumn, pbreakrecPrev, breakrecMacPrev,
breakrecMaxCurrent, pbreakrecCurrent, pbreakrecMacCurrent,
plslinfo, pplsline, &fSuccessful);
if (lserr != lserrNone)
return lserr;
if (!fSuccessful)
{ /* coefficient has not been sufficient before so increase it */
if (plsc->lMarginIncreaseCoefficient == LONG_MIN)
plsc->lMarginIncreaseCoefficient = 1;
else
{
if (plsc->lMarginIncreaseCoefficient >= uLsInfiniteRM / 2 )
plsc->lMarginIncreaseCoefficient = uLsInfiniteRM;
else
plsc->lMarginIncreaseCoefficient *= 2;
}
}
}
while (!fSuccessful);
#ifdef DEBUG
#ifdef LSTEST_GETMINDUR
/* Test LsGetMinDurBreaks () */
if ((lserr == lserrNone) && (plslinfo->endr != endrNormal) &&
(plslinfo->endr != endrHyphenated) && (! (plsc->grpfManager & fFmiDoHyphenation)) )
{
/* Line was ended with hard break / stopped */
long durMinInclTrail;
long durMinExclTrail;
lserr = LsGetMinDurBreaks ( plsc, *pplsline, &durMinInclTrail,
&durMinExclTrail );
};
#endif /* LSTEST_GETMINDUR */
#endif /* DEBUG */
return lserr;
}
/* ---------------------------------------------------------------------- */
/* C R E A T E L I N E C O R E*/
/*----------------------------------------------------------------------------
%%Function: CreateLineCore
%%Contact: igorzv
Parameters:
plsc - (IN) ptr to line services context
cpFirst - (IN) starting cp in line
duaColumn - (IN) column width in twips
pbreakrecPrev - (IN) previous line's break records
breakrecMacPrev - (IN) number of previous line's break records
breakrecMaxCurrent- (IN) size of the array of current line's break records
pbreakrecCurrent- (OUT) current line's break records
pbreakrecMacCurrent-(OUT) actual number of current line's break records
plsinfo - (OUT) visible line info to fill in
pplsline - (OUT) ptr to line opaque to client
pfSuccessful - (OUT) fSuccessful: false means insufficient fetch
Internal procedure organized to handle error in choosing extended right margin
----------------------------------------------------------------------------*/
static LSERR CreateLineCore(PLSC plsc,
LSCP cpFirst,
long duaColumn,
const BREAKREC* pbreakrecPrev,
DWORD breakrecMacPrev,
DWORD breakrecMaxCurrent,
BREAKREC* pbreakrecCurrent,
DWORD* pbreakrecMacCurrent,
LSLINFO* plslinfo,
PLSLINE* pplsline,
BOOL* pfSuccessful)
{
PLSLINE plsline;
LINEGEOMETRY lgeom;
LSCHP lschp;
LSERR lserr;
BOOL fGeneral = fFalse;
BOOL fHardStop;
BOOL fSuccessfulQuickBreak;
LSCP cpLimLine;
LSDCP dcpDepend = 0;
LSFRUN lsfrun;
long urFinalPen;
long urColumnMaxIncreased;
ENDRES endr = endrNormal;
/*Initialization; */
*pfSuccessful = fTrue;
lsfrun.plschp = &lschp; /* we use the same area for lschips */
/* because we pasing pointer to const nobody can change it */
plsline= PvNewQuick(plsc->pqhLines, cbRep(struct lsline, rgplnobj, plsc->lsiobjcontext.iobjMac));
if (plsline == NULL)
return lserrOutOfMemory;
plsc->lsstate = LsStateFormatting; /* We start here forwating line. After this momemt we must
free context before return. We do this either in CannotCreateLine (error)
or EndFormatting (success) */
plsc->plslineCur = plsline;
*pplsline = plsline;
lserr = InitCurLine (plsc, cpFirst);
if (lserr != lserrNone)
return CannotCreateLine(pplsline, lserr);
/* check initial value of flags */
Assert(FAllSimpleText(plsc));
Assert(!FNonRealDnodeEncounted(plsc));
Assert(!FNonZeroDvpPosEncounted(plsc));
Assert(AggregatedDisplayFlags(plsc) == 0);
Assert(!FNominalToIdealEncounted(plsc));
Assert(!FForeignObjectEncounted(plsc));
Assert(!FTabEncounted(plsc));
Assert(!FNonLeftTabEncounted(plsc));
Assert(!FSubmittedSublineEncounted(plsc));
Assert(!FAutodecimalTabPresent(plsc));
plsc->cLinesActive += 1;
lserr = InitTextParams(plsc, cpFirst, duaColumn, &lsfrun, &lschp, &lgeom);
if (lserr != lserrNone)
return CannotCreateLine(pplsline,lserr);
/* prepare starting set for formatting */
InitFormattingContext(plsc, lgeom.urLeft, lgeom.cpFirstVis);
/* REVIEW comments */
if (lgeom.fStopped)
{
plsc->lsstate = LsStateBreaking; /* we now in a stage of breaking */
lserr = FiniEndLine(plsc, endrStopped, lgeom.cpFirstVis, 0, plslinfo);
if (lserr != lserrNone)
return CannotCreateLine(pplsline,lserr);
else
return lserrNone;
}
/* change first character because of hyphenation */
if (lgeom.fYsrChangeAfter)
{
Assert(!(lgeom.fAutonumber) || (lgeom.fAutoDecimalTab));
lserr = FiniChangeAfter(plsc, &lsfrun, pbreakrecPrev,
breakrecMacPrev, breakrecMaxCurrent,
pbreakrecCurrent, pbreakrecMacCurrent, plslinfo, pfSuccessful);
if (lserr != lserrNone || !*pfSuccessful)
return CannotCreateLine(pplsline, lserr);
else
return lserrNone;
}
/* important note to understand code flow : The situation below can happened
only for first line in a paragraph, the situation above never can happened
for such line. */
/* if autonumbering or auto-decimal tab */
if ((lgeom.fAutonumber) || (lgeom.fAutoDecimalTab))
{
Assert(!lgeom.fYsrChangeAfter);
TurnOffAllSimpleText(plsc);
/* we will release plsrun in FiniAuto */
lserr = FiniAuto(plsc, lgeom.fAutonumber, lgeom.fAutoDecimalTab, &lsfrun,
lgeom.durAutoDecimalTab, pbreakrecPrev,
breakrecMacPrev, breakrecMaxCurrent,
pbreakrecCurrent, pbreakrecMacCurrent, plslinfo, pfSuccessful);
if (lserr != lserrNone || !*pfSuccessful)
return CannotCreateLine(pplsline, lserr);
else
return lserrNone;
}
if (FAdvancedTypographyEnabled(plsc, breakrecMacPrev ))
{
/* we should release run here, in general procedure we will fetch it again */
if (!plsc->fDontReleaseRuns)
{
lserr = plsc->lscbk.pfnReleaseRun(plsc->pols, lsfrun.plsrun);
if (lserr != lserrNone)
return CannotCreateLine(pplsline,lserr);
}
lserr = FiniFormatGeneralCase(plsc, pbreakrecPrev,
breakrecMacPrev, breakrecMaxCurrent,
pbreakrecCurrent, pbreakrecMacCurrent, plslinfo, pfSuccessful);
if (lserr != lserrNone || !*pfSuccessful)
return CannotCreateLine(pplsline,lserr);
else
return lserrNone;
}
/* it is possible that width of column is negative: in such scase we'll
use another right margin*/
if (plsc->urRightMarginBreak <= 0 && plsc->lMarginIncreaseCoefficient == LONG_MIN)
plsc->lMarginIncreaseCoefficient = 1;
if (plsc->lMarginIncreaseCoefficient != LONG_MIN)
{
urColumnMaxIncreased = RightMarginIncreasing(plsc, plsc->urRightMarginBreak);
}
else
{
urColumnMaxIncreased = plsc->urRightMarginBreak;
}
lserr = QuickFormatting(plsc, &lsfrun, urColumnMaxIncreased,
&fGeneral, &fHardStop, &cpLimLine, &urFinalPen);
if (lserr != lserrNone)
return CannotCreateLine(pplsline,lserr);
if (fGeneral)
{
lserr = FiniFormatGeneralCase(plsc, pbreakrecPrev,
breakrecMacPrev, breakrecMaxCurrent,
pbreakrecCurrent, pbreakrecMacCurrent,
plslinfo, pfSuccessful);
if (lserr != lserrNone || !*pfSuccessful)
return CannotCreateLine(pplsline, lserr);
else
return lserrNone;
}
plsc->lsstate = LsStateBreaking; /* we now in a stage of breaking */
if (FTryQuickBreak(plsc))
{
lserr = BreakQuickCase(plsc, fHardStop, &dcpDepend, &cpLimLine,
&fSuccessfulQuickBreak, &endr);
if (lserr != lserrNone)
return CannotCreateLine(pplsline,lserr);
}
else
{
fSuccessfulQuickBreak = fFalse;
}
if (fSuccessfulQuickBreak)
{
if (endr == endrNormal || endr == endrAltEndPara ||
(endr == endrEndPara && !plsc->fLimSplat))
{
lserr = EndFormatting(plsc, endr, cpLimLine,
dcpDepend, plslinfo);
if (lserr != lserrNone)
return CannotCreateLine(pplsline,lserr);
else
return lserrNone;
}
else /* there is splat that is handled in FiniEndLine */
{
lserr = FiniEndLine(plsc, endr, cpLimLine, dcpDepend, plslinfo);
if (lserr != lserrNone)
return CannotCreateLine(pplsline, lserr);
else
return lserrNone;
}
}
else
{
/* here we should use BreakGeneralCase */
lserr = BreakGeneralCase(plsc, fHardStop, breakrecMaxCurrent,
pbreakrecCurrent, pbreakrecMacCurrent,&dcpDepend,
&cpLimLine, &endr, pfSuccessful);
if (lserr != lserrNone || !*pfSuccessful)
return CannotCreateLine(pplsline,lserr);
lserr = FiniEndLine(plsc, endr, cpLimLine, dcpDepend, plslinfo);
if (lserr != lserrNone)
return CannotCreateLine(pplsline, lserr);
else
return lserrNone;
}
} /* end LsCreateLine */
/* ---------------------------------------------------------------------- */
/* F I N I F O R M A T G E N E R A L C A S E*/
/*----------------------------------------------------------------------------
%%Function: FiniFormatGeneralCase
%%Contact: igorzv
Parameters:
plsc - (IN) ptr to line services context
pbreakrecPrev - (IN) previous line's break records
breakrecMacPrev - (IN) number of previous line's break records
breakrecMaxCurrent- (IN) size of the array of current line's break records
pbreakrecCurrent- (OUT) current line's break records
pbreakrecMacCurrent-(OUT) actual number of current line's break records
plsinfo - (OUT) visible line info to fill in
pfSuccessful - (OUT) fSuccessful: false means insufficient fetch
Formatting and breaking in a case when "quick formatting" is prohibit
----------------------------------------------------------------------------*/
static LSERR FiniFormatGeneralCase (PLSC plsc,
const BREAKREC* pbreakrecPrev,
DWORD breakrecMacPrev,
DWORD breakrecMaxCurrent,
BREAKREC* pbreakrecCurrent,
DWORD* pbreakrecMacCurrent,
LSLINFO* plslinfo, BOOL* pfSuccessful)
{
long urColumnMaxIncreased;
FMTRES fmtres;
LSERR lserr;
LSCP cpLimLine;
LSDCP dcpDepend;
PLSDNODE plsdnFirst, plsdnLast;
long urFinal;
ENDRES endr;
Assert(FIsLSC(plsc));
Assert(FFormattingAllowed(plsc));
Assert(plslinfo != NULL);
*pfSuccessful = fTrue;
if (plsc->lMarginIncreaseCoefficient == LONG_MIN) /* we are here for the first time */
{
/* increase right margin for nominal to ideal and compression */
if (!FBreakJustSimple(plsc->lsadjustcontext.lsbrj))
plsc->lMarginIncreaseCoefficient = 2;
else
plsc->lMarginIncreaseCoefficient = 1;
}
urColumnMaxIncreased = RightMarginIncreasing(plsc, plsc->urRightMarginBreak);
if (FNominalToIdealBecauseOfParagraphProperties(plsc->grpfManager,
plsc->lsadjustcontext.lskj))
TurnOnNominalToIdealEncounted(plsc);
if (breakrecMacPrev != 0)
lserr = FetchAppendEscResumeCore(plsc, urColumnMaxIncreased, NULL, 0,
pbreakrecPrev, breakrecMacPrev,
&fmtres, &cpLimLine, &plsdnFirst,
&plsdnLast, &urFinal);
else
lserr = FetchAppendEscCore(plsc, urColumnMaxIncreased, NULL, 0,
&fmtres, &cpLimLine, &plsdnFirst,
&plsdnLast, &urFinal);
if (lserr != lserrNone)
return lserr;
/* fetch append esc can be stopped because of tab */
/* so we have loop for tabs here */
while (fmtres == fmtrTab)
{
lserr = HandleTab(plsc);
if (lserr != lserrNone)
return lserr;
if (FBreakthroughLine(plsc))
{
urColumnMaxIncreased = RightMarginIncreasing(plsc, plsc->urRightMarginBreak);
}
lserr = FetchAppendEscCore(plsc, urColumnMaxIncreased, NULL, 0,
&fmtres, &cpLimLine, &plsdnFirst,
&plsdnLast, &urFinal);
if (lserr != lserrNone)
return lserr;
}
Assert(fmtres == fmtrStopped || fmtres == fmtrExceededMargin);
/* skip back pen dnodes */
while (plsdnLast != NULL && FIsDnodePen(plsdnLast))
{
plsdnLast = plsdnLast->plsdnPrev;
}
/* close last border */
if (FDnodeHasBorder(plsdnLast) && !FIsDnodeCloseBorder(plsdnLast))
{
lserr = CloseCurrentBorder(plsc);
if (lserr != lserrNone)
return lserr;
}
if (fmtres == fmtrExceededMargin
&& (urFinal <= plsc->urRightMarginBreak /* it's important for truncation to have <= here */
|| plsdnLast == NULL || FIsNotInContent(plsdnLast) /* this can happen if in nominal
to ideal (dcpMaxContext) we deleted everything
in content, but starting point
of content is already behind right margin */
)
)
{
/* return unsuccessful */
*pfSuccessful = fFalse;
return lserrNone;
}
else
{
plsc->lsstate = LsStateBreaking; /* we now in a stage of breaking */
lserr = BreakGeneralCase(plsc, (fmtres == fmtrStopped), breakrecMaxCurrent,
pbreakrecCurrent, pbreakrecMacCurrent,
&dcpDepend, &cpLimLine, &endr, pfSuccessful);
if (lserr != lserrNone || !*pfSuccessful)
return lserr;
/* because, we work with increased margin we can resolve pending tab only after break */
/* we use here that after breaking decision we set state after break point*/
lserr = HandleTab(plsc);
if (lserr != lserrNone)
return lserr;
return FiniEndLine(plsc, endr, cpLimLine, dcpDepend, plslinfo);
}
}
/* ---------------------------------------------------------------------- */
/* F I N I E N D L I N E */
/*----------------------------------------------------------------------------
%%Function: FiniEndLine
%%Contact: igorzv
Parameters:
plsc - (IN) ptr to line services context
endr - (IN) how the line ended
cpLimLine - (IN) cpLim of a line as a result of breaking,
can be changed in this procedure
dcpDepend - (IN) amount of characters after breaking point
that has participated in breaking decision,
can be changed in this procedure
plsinfo - (OUT) visible line info to fill in
Handles splat, calculates heights, special effects
----------------------------------------------------------------------------*/
static LSERR FiniEndLine(PLSC plsc, ENDRES endr, LSCP cpLimLine,
LSDCP dcpDepend, LSLINFO* plslinfo)
{
LSLINFO* plslinfoState;
OBJDIM objdim;
LSERR lserr;
PLSLINE plsline;
BOOL fEmpty;
ENDRES endrOld;
Assert(FIsLSC(plsc));
Assert(plslinfo != NULL);
plsline = plsc->plslineCur;
plslinfoState = &(plsline->lslinfo);
endrOld = endr;
if (endr == endrEndPara && plsc->fLimSplat)
{
endr = endrEndParaSection;
cpLimLine++;
}
/* handling splat */
if (endr == endrEndColumn || endr == endrEndSection ||
endr == endrEndParaSection|| endr == endrEndPage)
{
if (plsc->grpfManager & fFmiVisiSplats)
{
switch (endr)
{
case endrEndColumn: plsline->kspl = ksplColumnBreak; break;
case endrEndSection: plsline->kspl = ksplSectionBreak; break;
case endrEndParaSection: plsline->kspl = ksplSectionBreak; break;
case endrEndPage: plsline->kspl = ksplPageBreak; break;
}
}
lserr = FIsSublineEmpty(GetMainSubline(plsc), &fEmpty);
if (lserr != lserrNone)
return lserr;
if (!fEmpty && (plsc->grpfManager & fFmiAllowSplatLine))
{
cpLimLine--;
dcpDepend++;
plsline->kspl = ksplNone;
if (endrOld == endrEndPara)
{
endr = endrEndPara;
}
else
{
endr = endrNormal;
}
}
}
/* Height calculation; */
lserr = GetObjDimSublineCore(GetMainSubline(plsc), &objdim);
if (lserr != lserrNone)
return lserr;
plslinfoState->dvrAscent = objdim.heightsRef.dvAscent;
plslinfoState->dvpAscent = objdim.heightsPres.dvAscent;
plslinfoState->dvrDescent = objdim.heightsRef.dvDescent;
plslinfoState->dvpDescent = objdim.heightsPres.dvDescent;
plslinfoState->dvpMultiLineHeight = objdim.heightsPres.dvMultiLineHeight;
plslinfoState->dvrMultiLineHeight = objdim.heightsRef.dvMultiLineHeight;
/* calculation plslinfoState->EffectsFlags*/
if (plslinfoState->EffectsFlags) /* some run with special effects happend during formating */
{
lserr = GetSpecialEffectsSublineCore(GetMainSubline(plsc),
&plsc->lsiobjcontext, &plslinfoState->EffectsFlags);
if (lserr != lserrNone)
return lserr;
}
return EndFormatting(plsc, endr, cpLimLine, dcpDepend, plslinfo);
}
/* ---------------------------------------------------------------------- */
/* F I N I A U T O */
/*----------------------------------------------------------------------------
%%Function: FiniAuto
%%Contact: igorzv
Parameters:
plsc - (IN) ptr to line services context
fAutonumber - (IN) does this line containes autonimber
fAutoDecimaltab - (IN) does this line containes autodecimal tab
plsfrunMainText - (IN) first run of main text
durAutoDecimalTab- (IN) tab stop for autodecimal tab
pbreakrecPrev - (IN) previous line's break records
breakrecMacPrev - (IN) number of previous line's break records
breakrecMaxCurrent- (IN) size of the array of current line's break records
pbreakrecCurrent- (OUT) current line's break records
pbreakrecMacCurrent-(OUT) actual number of current line's break records
plsinfo - (OUT) visible line info to fill in
pfSuccessful - (OUT) fSuccessful: false means insufficient fetch
Completes CreateLine logic for autonumbering and auto-decimal tab
----------------------------------------------------------------------------*/
static LSERR FiniAuto(
PLSC plsc,
BOOL fAutonumber,
BOOL fAutoDecimalTab,
PLSFRUN plsfrunMainText,
long durAutoDecimalTab,
const BREAKREC* pbreakrecPrev,
DWORD breakrecMacPrev,
DWORD breakrecMaxCurrent,
BREAKREC* pbreakrecCurrent,
DWORD* pbreakrecMacCurrent,
LSLINFO* plslinfo, BOOL* pfSuccessful)
{
LSERR lserr;
if (plsc->lMarginIncreaseCoefficient == LONG_MIN)
plsc->lMarginIncreaseCoefficient = 1;
if (fAutonumber) /*autonumbering */
{
lserr = FormatAnm(plsc, plsfrunMainText);
if (lserr != lserrNone)
{
return ErrReleasePreFetchedRun(plsc, plsfrunMainText->plsrun, lserr);
}
}
if (fAutoDecimalTab)
{
lserr = InitializeAutoDecTab(plsc, durAutoDecimalTab);
if (lserr != lserrNone)
{
return ErrReleasePreFetchedRun(plsc, plsfrunMainText->plsrun, lserr);
}
}
/* we should release run here, in general procedure we will fetch it again */
if (!plsc->fDontReleaseRuns)
{
lserr = plsc->lscbk.pfnReleaseRun(plsc->pols, plsfrunMainText->plsrun);
if (lserr != lserrNone)
return lserr;
}
return FiniFormatGeneralCase(plsc, pbreakrecPrev,
breakrecMacPrev, breakrecMaxCurrent,
pbreakrecCurrent, pbreakrecMacCurrent, plslinfo, pfSuccessful);
}
/* F I N I C H A N G E A F T E R */
/*----------------------------------------------------------------------------
%%Function: FiniChangeAfter
%%Contact: igorzv
Parameters:
plsc - (IN) ptr to line services context
plsfrun, - (IN) lsfrun of modified first run
pbreakrecPrev - (IN) previous line's break records
breakrecMacPrev - (IN) number of previous line's break records
breakrecMaxCurrent- (IN) size of the array of current line's break records
pbreakrecCurrent- (OUT) current line's break records
pbreakrecMacCurrent-(OUT) actual number of current line's break records
plsinfo - (OUT) visible line info to fill in
pfSuccessful - (OUT) fSuccessful: false means insufficient fetch
Completes CreateLine logic for change after because of hyphenation
----------------------------------------------------------------------------*/
static LSERR FiniChangeAfter(PLSC plsc, LSFRUN* plsfrun, const BREAKREC* pbreakrecPrev,
DWORD breakrecMacPrev,
DWORD breakrecMaxCurrent,
BREAKREC* pbreakrecCurrent,
DWORD* pbreakrecMacCurrent,
LSLINFO* plslinfo, BOOL* pfSuccessful)
{
LSERR lserr;
FMTRES fmtres;
lserr = ProcessOneRun(plsc, plsc->urRightMarginBreak, plsfrun, NULL, 0, &fmtres);
if (lserr != lserrNone)
return lserr;
return FiniFormatGeneralCase(plsc, pbreakrecPrev,
breakrecMacPrev, breakrecMaxCurrent,
pbreakrecCurrent, pbreakrecMacCurrent, plslinfo, pfSuccessful);
}
/* ---------------------------------------------------------------------- */
/* E N D F O R M A T T I N G*/
/*----------------------------------------------------------------------------
%%Function: EndFormatting
%%Contact: igorzv
Parameters:
plsc - (IN) ptr to line services context
endres - (IN) how line ends
cpLimLine - (IN) cpLim of a line as a result of breaking,
can be changed in this procedure
dcpDepend - (IN) amount of characters after breaking point
that has participated in breaking decision,
can be changed in this procedure
plsinfo - (OUT) visible line info to fill in
Filles in lslinfo
----------------------------------------------------------------------------*/
static LSERR EndFormatting (PLSC plsc, enum endres endr,
LSCP cpLimLine, LSDCP dcpDepend, LSLINFO* plslinfo)
{
PLSLINE plsline = plsc->plslineCur;
LSLINFO* plslinfoContext = &(plsline->lslinfo);
Assert(FIsLSC(plsc));
Assert(plslinfo != NULL);
plslinfoContext->cpLim = cpLimLine;
plslinfoContext->dcpDepend = dcpDepend;
plslinfoContext->endr = endr;
*plslinfo = *plslinfoContext;
plsc->lsstate = LsStateFree; /* we always return through this procedure (in a case of success )
so we free context here */
return lserrNone;
}
/*----------------------------------------------------------------------------
/* L S M O D I F Y L I N E H E I G H T */
/*----------------------------------------------------------------------------
%%Function: LsModifyLineHeight
%%Contact: igorzv
Parameters:
plsc - (IN) ptr to line services context
psline - (IN) ptr to a line to be modified
dvpAbove - (IN) dvpAbove to set in plsline
dvpAscent - (IN) dvpAscent to set in plsline
dvpDescent - (IN) dvpDescent to set in plsline
dvpBelow - (IN) dvpBelow to set in plsline
An exported LineServices API.
Modifies heights in a plsline structure
----------------------------------------------------------------------------*/
LSERR WINAPI LsModifyLineHeight(PLSC plsc,
PLSLINE plsline,
long dvpAbove,
long dvpAscent,
long dvpDescent,
long dvpBelow)
{
if (!FIsLSC(plsc))
return lserrInvalidContext;
if (!FIsLSLINE(plsline))
return lserrInvalidLine;
if (plsline->lssubl.plsc != plsc)
return lserrMismatchLineContext;
if (plsc->lsstate != LsStateFree)
return lserrContextInUse;
plsline->dvpAbove = dvpAbove;
plsline->lslinfo.dvpAscent = dvpAscent;
plsline->lslinfo.dvpDescent = dvpDescent;
plsline->dvpBelow = dvpBelow;
return lserrNone;
}
/*----------------------------------------------------------------------------
/* L S D E S T R O Y L I N E */
/*----------------------------------------------------------------------------
%%Function: LsDestroyLine
%%Contact: igorzv
Parameters:
plsc - (IN) ptr to line services context
psline - (IN) ptr to a line to be deleted
An exported LineServices API.
Removed plsline structure , dnode list, line objects structures
----------------------------------------------------------------------------*/
LSERR WINAPI LsDestroyLine(PLSC plsc, /* IN: ptr to line services context */
PLSLINE plsline) /* IN: ptr to line -- opaque to client */
{
POLS pols;
LSERR lserrNew, lserr = lserrNone;
if (!FIsLSC(plsc))
return lserrInvalidContext;
if (!FIsLSLINE(plsline))
return lserrInvalidLine;
if (plsline->lssubl.plsc != plsc)
return lserrMismatchLineContext;
if (plsc->lsstate != LsStateFree)
return lserrContextInUse;
Assert(FIsLsContextValid(plsc));
Assert(plsc->cLinesActive > 0);
plsc->lsstate = LsStateDestroyingLine;
pols = plsc->pols;
/* optimization */
/* we use here that text doesn't have pinfosubl and DestroyDobj is actually empty for text */
if (!plsc->fDontReleaseRuns || !plsline->fAllSimpleText)
{
lserrNew = DestroyDnodeList(&plsc->lscbk, plsc->pols, &plsc->lsiobjcontext,
plsline->lssubl.plsdnFirst, plsc->fDontReleaseRuns);
if (lserrNew != lserrNone && lserr == lserrNone)
lserr = lserrNew;
}
if (plsline == plsc->plslineCur)
plsc->plslineCur = NULL;
lserrNew = RemoveLineObjects(plsline);
if (lserrNew != lserrNone && lserr == lserrNone)
lserr = lserrNew;
/* flush heap of dnodes */
if (plsline->pqhAllDNodes != NULL)
FlushQuickHeap(plsline->pqhAllDNodes);
if (plsc->pqhAllDNodesRecycled != NULL)
DestroyQuickHeap(plsc->pqhAllDNodesRecycled);
/* recycle quick heap of dnodes */
plsc->pqhAllDNodesRecycled = plsline->pqhAllDNodes;
plsline->tag = tagInvalid;
DisposeQuickPv(plsc->pqhLines, plsline,
cbRep(struct lsline, rgplnobj, plsc->lsiobjcontext.iobjMac));
plsc->cLinesActive -= 1;
plsc->lsstate = LsStateFree;
return lserr;
}
/* ---------------------------------------------------------------------- */
/* L S G E T L I N E D U R */
/*----------------------------------------------------------------------------
%%Function: LsGetLineDur
%%Contact: igorzv
Parameters:
plsc - (IN) LS context
plsline - (IN) ptr to a line
pdurInclTrail - (OUT) dur of line incl. trailing area
pdurExclTrail - (OUT) dur of line excl. trailing area
----------------------------------------------------------------------------*/
LSERR WINAPI LsGetLineDur (PLSC plsc, PLSLINE plsline,
long* pdurInclTrail, long* pdurExclTrail)
{
LSERR lserr;
if (!FIsLSC(plsc))
return lserrInvalidContext;
if (!FIsLSLINE(plsline))
return lserrInvalidLine;
if (plsline->lssubl.plsc != plsc)
return lserrMismatchLineContext;
if (plsc->lsstate != LsStateFree)
return lserrContextInUse;
Assert(FIsLsContextValid(plsc));
Assert(plsc->cLinesActive > 0);
/* check that the line is active */
if (plsline != plsc->plslineCur)
return lserrLineIsNotActive;
/* set breaking state */
plsc->lsstate = LsStateBreaking;
lserr = GetLineDurCore(plsc, pdurInclTrail, pdurExclTrail);
plsc->lsstate = LsStateFree;
return lserr;
}
/* ---------------------------------------------------------------------- */
/* L S G E T M I N D U R B R E A K S */
/*----------------------------------------------------------------------------
%%Function: LsGetMinDurBreaks
%%Contact: igorzv
Parameters:
plsc - (IN) LS context
plsline - (IN) ptr to a line
pdurMinInclTrail - (OUT) min dur between breaks including trailing area
pdurMinExclTrail - (OUT) min dur between breaks excluding trailing area
----------------------------------------------------------------------------*/
LSERR WINAPI LsGetMinDurBreaks (PLSC plsc, PLSLINE plsline,
long* pdurMinInclTrail, long* pdurMinExclTrail)
{
LSERR lserr;
if (!FIsLSC(plsc))
return lserrInvalidContext;
if (!FIsLSLINE(plsline))
return lserrInvalidLine;
if (plsline->lssubl.plsc != plsc)
return lserrMismatchLineContext;
if (plsc->lsstate != LsStateFree)
return lserrContextInUse;
Assert(FIsLsContextValid(plsc));
Assert(plsc->cLinesActive > 0);
/* check that the line is active */
if (plsline != plsc->plslineCur)
return lserrLineIsNotActive;
/* set breaking state */
plsc->lsstate = LsStateBreaking;
lserr = GetMinDurBreaksCore(plsc, pdurMinInclTrail, pdurMinExclTrail);
plsc->lsstate = LsStateFree;
return lserr;
}
/*----------------------------------------------------------------------*/
#define grpfTextMask ( \
fFmiVisiCondHyphens | \
fFmiVisiParaMarks | \
fFmiVisiSpaces | \
fFmiVisiTabs | \
fFmiVisiBreaks | \
fFmiDoHyphenation | \
fFmiWrapTrailingSpaces | \
fFmiWrapAllSpaces | \
fFmiPunctStartLine | \
fFmiHangingPunct | \
fFmiApplyBreakingRules | \
fFmiFCheckTruncateBefore | \
fFmiDrawInCharCodes | \
fFmiSpacesInfluenceHeight | \
fFmiIndentChangesHyphenZone | \
fFmiNoPunctAfterAutoNumber | \
fFmiTreatHyphenAsRegular \
)
/*----------------------------------------------------------------------------*/
/* I N I T T E X T P A R A M S */
/*----------------------------------------------------------------------------
%%Function: InitTextParams
%%Contact: igorzv
Parameters:
plsc - (IN) ptr to line services context
cp, - (IN) cp to start fetch
duaCol - (IN) duaColumn
plsfrun - (IN) lsfrun of the first run
plschp - (OUT) lsfrun of the first run
plgeom - (OUT) set of flags and parameters about a line
LsCreateLine calls this function at the beginning of the line in order
to skip over vanished text, fetch an LSPAP, and invoke the text APIs
SetTextLineParams().
----------------------------------------------------------------------------*/
static LSERR InitTextParams(PLSC plsc, LSCP cp, long duaCol,
LSFRUN* plsfrun, PLSCHP plschp, LINEGEOMETRY* plgeom)
{
LSERR lserr;
LSPAP lspap;
POLS pols = plsc->pols;
BOOL fFirstLineInPara;
BOOL fHidden;
BOOL fStopped = fFalse;
BOOL fNoLinesParaBefore;
BOOL fNewPara;
plsfrun->lpwchRun = NULL;
plsfrun->plsrun = NULL;
plsfrun->cwchRun = 0;
plgeom->fYsrChangeAfter = fFalse;
Assert(cp >= 0);
lserr = plsc->lscbk.pfnFetchPap(pols, cp, &lspap);
if (lserr != lserrNone)
return lserr;
if (FPapInconsistent(&lspap))
return lserrInvalidPap;
/* N.B. lspap.cpFirstContent may be negative, which indicates
* "no content in this paragraph".
*/
fNoLinesParaBefore = lspap.cpFirstContent < 0 || cp <= lspap.cpFirstContent;
if (!fNoLinesParaBefore && (lspap.grpf & fFmiDoHyphenation))
{
lserr = GetYsrChangeAfterRun(plsc, cp, &plgeom->fYsrChangeAfter, plsfrun, plschp, plgeom);
if (lserr != lserrNone)
return lserr;
if (plgeom->fYsrChangeAfter)
{
fFirstLineInPara = fFalse;
fStopped = fFalse;
lserr = FillTextParams(plsc, cp, duaCol, &lspap, fFirstLineInPara,
fStopped, plgeom);
if (lserr != lserrNone)
return ErrReleasePreFetchedRun(plsc, plsfrun->plsrun, lserr);
else
return lserrNone;
}
}
lserr = plsc->lscbk.pfnFetchRun(pols, cp,&plsfrun->lpwchRun, &plsfrun->cwchRun,
&fHidden, plschp, &plsfrun->plsrun);
if (lserr != lserrNone)
return lserr;
if (fHidden) /* vanished text */
{
lserr = FetchUntilVisible(plsc, &lspap, &cp, plsfrun, plschp,
&fStopped, &fNewPara);
if (lserr != lserrNone)
return lserr;
if (fNewPara)
fNoLinesParaBefore = fTrue;
}
fFirstLineInPara = fNoLinesParaBefore && FBetween(lspap.cpFirstContent, 0, cp);
lserr = FillTextParams(plsc, cp, duaCol, &lspap, fFirstLineInPara,
fStopped, plgeom);
if (lserr != lserrNone)
return ErrReleasePreFetchedRun(plsc, plsfrun->plsrun, lserr);
else
return lserrNone;
}
/*----------------------------------------------------------------------------*/
/* G E T Y S R C H A N G E A F T E R R U N */
/*----------------------------------------------------------------------------
%%Function: GetYsrChangeAfterRun
%%Contact: igorzv
Parameters:
plsc - (IN) ptr to line services context
cp, - (IN) cp to start fetch
pfYsrChangeAfter (OUT) is it hyphenation of the previous line
plsfrun, (OUT) lsfrun of modified first run
plschp (OUT) lschp of modified first run
plsgeom (OUT) to put wchYsr
InitTextParams calls this procedure if there is a possibility
for previous line to be hyphenated.
If previous line has been hyphenated with ysr change after procedure returns
modified first run for a line
----------------------------------------------------------------------------*/
static LSERR GetYsrChangeAfterRun(PLSC plsc, LSCP cp, BOOL* pfYsrChangeAfter,
PLSFRUN plsfrun, PLSCHP plschp, LINEGEOMETRY* plgeom)
{
LSFRUN lsfrunPrev;
LSCHP lschpPrev;
BOOL fHidden;
LSERR lserr;
lsfrunPrev.plschp = &lschpPrev;
*pfYsrChangeAfter = fFalse;
/* Fetch run at cp-1 to handle ysrChangeAfter from previous line.
*/
lserr = plsc->lscbk.pfnFetchRun(plsc->pols, cp-1, &lsfrunPrev.lpwchRun,
&lsfrunPrev.cwchRun, &fHidden,
&lschpPrev, &lsfrunPrev.plsrun);
if (lserr != lserrNone)
return lserr;
/* previous run is hyphenated text */
if (!fHidden && ((lsfrunPrev.plschp)->idObj == idObjTextChp)
&& (lsfrunPrev.plschp)->fHyphen)
{
DWORD kysr;
WCHAR wchYsr;
Assert(lsfrunPrev.cwchRun == 1);
lserr = plsc->lscbk.pfnGetHyphenInfo(plsc->pols, lsfrunPrev.plsrun, &kysr, &wchYsr);
if (lserr != lserrNone)
return ErrReleasePreFetchedRun(plsc, lsfrunPrev.plsrun, lserr);
if ((kysr == kysrChangeAfter) &&
(wchYsr != 0))
{
lserr = plsc->lscbk.pfnFetchRun(plsc->pols, cp, &plsfrun->lpwchRun,
&plsfrun->cwchRun, &fHidden,
plschp, &plsfrun->plsrun);
if (lserr != lserrNone)
return ErrReleasePreFetchedRun(plsc, lsfrunPrev.plsrun, lserr);
if (!fHidden)
{
Assert((plsfrun->plschp)->idObj == idObjTextChp);
plgeom->wchYsr = wchYsr;
/* Synthesize a 1-byte run */
plsfrun->lpwchRun = &plgeom->wchYsr; /* here is the only reason to keep wchrChar in lgeom
we cann't use local memory to keep it */
plsfrun->cwchRun = 1;
plschp->fHyphen = kysrNil;
*pfYsrChangeAfter = fTrue;
}
else
{
if (!plsc->fDontReleaseRuns)
{
lserr = plsc->lscbk.pfnReleaseRun(plsc->pols, plsfrun->plsrun);
if (lserr != lserrNone)
return ErrReleasePreFetchedRun(plsc, lsfrunPrev.plsrun, lserr);
}
}
}
}
/* Release run from previous line */
if (!plsc->fDontReleaseRuns)
{
lserr = plsc->lscbk.pfnReleaseRun(plsc->pols, lsfrunPrev.plsrun);
if (lserr != lserrNone)
return ErrReleasePreFetchedRun(plsc, plsfrun->plsrun, lserr);
}
return lserrNone;
}
/*----------------------------------------------------------------------------*/
/* F I L L T E X T P A R A M S */
/*----------------------------------------------------------------------------
%%Function: FillTextParamsTextParams
%%Contact: igorzv
Parameters:
plsc - (IN) ptr to line services context
cp, - (IN) cp to start fetch
duaCol - (IN) duaColumn
plspap - (IN) paragraph properties
fFirstLineInPara- (IN) flag fFirstLineInPara
fStopped - (IN) flag fStopped
plgeom - (OUT) set of flags and parameters about a line
LsCreateLine calls this function at the beginning of the line in order
to skip over vanished text, fetch an LSPAP, and invoke the text APIs
SetTextLineParams().
----------------------------------------------------------------------------*/
static LSERR FillTextParams(PLSC plsc, LSCP cp, long duaCol, PLSPAP plspap,
BOOL fFirstLineInPara, BOOL fStopped, LINEGEOMETRY* plgeom)
{
LSERR lserr;
TLPR tlpr;
DWORD iobjText;
PILSOBJ pilsobjText;
PLNOBJ plnobjText;
long uaLeft;
PLSLINE plsline = plsc->plslineCur;
long duaColumnMaxBreak;
long duaColumnMaxJustify;
long urLeft;
/* Copy information from lspap to context current line and local for LsCreateLine structure lgeom */
uaLeft = plspap->uaLeft;
if (fFirstLineInPara)
uaLeft += plspap->duaIndent;
urLeft = UrFromUa(LstflowFromSubline(GetMainSubline(plsc)),
&plsc->lsdocinf.lsdevres, uaLeft);
/* line */
plsline->lslinfo.fFirstLineInPara = fFirstLineInPara;
plsline->lslinfo.cpFirstVis = cp;
plsline->lssubl.lstflow = plspap->lstflow;
if (duaCol != uLsInfiniteRM && plspap->uaRightBreak < uLsInfiniteRM
&& plspap->uaRightJustify < uLsInfiniteRM)
{
duaColumnMaxBreak = duaCol - plspap->uaRightBreak;
duaColumnMaxJustify = duaCol - plspap->uaRightJustify;
}
else{
if (duaCol == uLsInfiniteRM)
{
duaColumnMaxBreak = uLsInfiniteRM;
duaColumnMaxJustify = uLsInfiniteRM;
}
else
{
if (plspap->uaRightBreak >= uLsInfiniteRM)
duaColumnMaxBreak = uLsInfiniteRM;
else
duaColumnMaxBreak = duaCol - plspap->uaRightBreak;
if (plspap->uaRightJustify >= uLsInfiniteRM)
duaColumnMaxJustify = uLsInfiniteRM;
else
duaColumnMaxJustify = duaCol - plspap->uaRightJustify;
}
}
/* fill in context for adjustment */
SetLineLineContainsAutoNumber(plsc, (plspap->grpf & fFmiAnm) && fFirstLineInPara);
SetUnderlineTrailSpacesRM(plsc, plspap->grpf & fFmiUnderlineTrailSpacesRM);
SetForgetLastTabAlignment(plsc, plspap->grpf & fFmiForgetLastTabAlignment);
plsc->lsadjustcontext.lskj = plspap->lskj;
plsc->lsadjustcontext.lskalign = plspap->lskal;
plsc->lsadjustcontext.lsbrj = plspap->lsbrj;
plsc->lsadjustcontext.urLeftIndent = urLeft;
plsc->lsadjustcontext.urStartAutonumberingText =0;
plsc->lsadjustcontext.urStartMainText = urLeft; /* autonumber can change it later */
if (duaColumnMaxJustify != uLsInfiniteRM)
{
plsc->lsadjustcontext.urRightMarginJustify = UrFromUa(
LstflowFromSubline(GetMainSubline(plsc)),
&(plsc->lsdocinf.lsdevres), duaColumnMaxJustify);
}
else
{
plsc->lsadjustcontext.urRightMarginJustify = uLsInfiniteRM;
}
if (duaColumnMaxBreak != uLsInfiniteRM)
{
plsc->urRightMarginBreak = UrFromUa(LstflowFromSubline(GetMainSubline(plsc)),
&(plsc->lsdocinf.lsdevres), duaColumnMaxBreak);
}
else
{
plsc->urRightMarginBreak = uLsInfiniteRM;
}
plsc->fIgnoreSplatBreak = plspap->grpf & fFmiIgnoreSplatBreak;
plsc->grpfManager = plspap->grpf;
plsc->fLimSplat = plspap->grpf & fFmiLimSplat;
plsc->urHangingTab = UrFromUa(LstflowFromSubline(GetMainSubline(plsc)),
&(plsc->lsdocinf.lsdevres), plspap->uaLeft);
/* snap grid */
if (plspap->lskj == lskjSnapGrid)
{
if (duaCol != uLsInfiniteRM)
{
plsc->lsgridcontext.urColumn = UrFromUa(
LstflowFromSubline(GetMainSubline(plsc)),
&(plsc->lsdocinf.lsdevres), duaCol);
}
else
{
plsc->lsgridcontext.urColumn = uLsInfiniteRM;
}
}
/* lgeom */
plgeom->cpFirstVis = cp;
plgeom->urLeft = urLeft;
plgeom->fAutonumber = (plspap->grpf & fFmiAnm) && fFirstLineInPara;
if (plspap->grpf & fFmiAutoDecimalTab)
{
plgeom->fAutoDecimalTab = fTrue;
plgeom->durAutoDecimalTab = UrFromUa(LstflowFromSubline(GetMainSubline(plsc)),
&(plsc->lsdocinf.lsdevres), plspap->duaAutoDecimalTab);
}
else
{
plgeom->fAutoDecimalTab = fFalse;
plgeom->durAutoDecimalTab = LONG_MIN;
}
plgeom->fStopped = fStopped;
/* prepare tlpr for text */
tlpr.grpfText = (plspap->grpf & grpfTextMask);
tlpr.fSnapGrid = (plspap->lskj == lskjSnapGrid);
tlpr.duaHyphenationZone = plspap->duaHyphenationZone;
tlpr.lskeop = plspap->lskeop;
/* we know that here is the first place we need plnobjText and we are creating it */
iobjText = IobjTextFromLsc(&(plsc->lsiobjcontext));
Assert( PlnobjFromLsline(plsline,iobjText) == NULL);
pilsobjText = PilsobjFromLsc(&(plsc->lsiobjcontext), iobjText);
lserr = CreateLNObjText(pilsobjText, &(plsline->rgplnobj[iobjText]));
if (lserr != lserrNone)
return lserr;
plnobjText = PlnobjFromLsline(plsline, iobjText);
lserr = SetTextLineParams(plnobjText, &tlpr);
if (lserr != lserrNone)
return lserr;
return lserrNone;
}
/*----------------------------------------------------------------------------
/* F E T C H U N T I L V I S I B L E */
/*----------------------------------------------------------------------------
%%Function: FetchUntilVisible
%%Contact: igorzv
Parameters:
plsc - (IN) ptr to line services context
plspap - (IN/OUT) current lspap before and after
pcp - (IN/OUT) current cp before and after
plsfrun - (IN/OUT) current lsfrun before and after
plschp - (IN/OUT) current lschp before and after
pfStopped - (OUT) fStopped: procedure stopped fetching because has not been allowed
to go across paragraph boundaries (result CheckPara Boundaries)
pfNewPara - (OUT) fNewPara: procedure crossed paragraph boundaries
Releases the supplied PLSRUN, if any, and fetches runs, starting at
the supplied cp, until a non-vanished run is fetched. As paragraph
boundaries are crossed, the LSPAP is updated.
----------------------------------------------------------------------------*/
static LSERR FetchUntilVisible(PLSC plsc, LSPAP* plspap, LSCP* pcp,
LSFRUN* plsfrun, PLSCHP plschp,
BOOL* pfStopped, BOOL* pfNewPara)
{
LSERR lserr;
LSCP dcpPrevRun = plsfrun->cwchRun;
BOOL fHidden;
*pfStopped = fFalse;
*pfNewPara = fFalse;
/* we assume here that this finction is called only after hidden run has been fetched
and such run is passed as an input parameter */
do
{
const PLSRUN plsrunT = plsfrun->plsrun;
*pcp += dcpPrevRun;
lserr = plsc->lscbk.pfnCheckParaBoundaries(plsc->pols, *pcp - dcpPrevRun, *pcp, pfStopped);
if (lserr != lserrNone)
return ErrReleasePreFetchedRun(plsc, plsrunT, lserr);
if (*pfStopped)
return lserrNone;
lserr = plsc->lscbk.pfnFetchPap(plsc->pols, *pcp, plspap);
if (lserr != lserrNone)
return ErrReleasePreFetchedRun(plsc, plsrunT, lserr);
if (FPapInconsistent(plspap))
return ErrReleasePreFetchedRun(plsc, plsrunT, lserrInvalidPap);
if ((*pcp - dcpPrevRun) < plspap->cpFirst)
*pfNewPara = fTrue;
plsfrun->plsrun = NULL;
if (plsrunT != NULL && !plsc->fDontReleaseRuns)
{
lserr = plsc->lscbk.pfnReleaseRun(plsc->pols, plsrunT);
if (lserr != lserrNone)
return lserr;
}
lserr = plsc->lscbk.pfnFetchRun(plsc->pols, *pcp,
&plsfrun->lpwchRun,
&plsfrun->cwchRun,
&fHidden,
plschp,
&plsfrun->plsrun);
if (lserr != lserrNone)
return lserr;
dcpPrevRun = plsfrun->cwchRun;
}
while (fHidden);
return lserrNone;
}
/* I N I T C U R L I N E */
/*----------------------------------------------------------------------------
%%Function: InitCurLine
%%Contact: igorzv
Parameters:
plsc - (IN) ptr to line services context
cpFirst - (IN) starting cp for a line
Set default value in lsline
----------------------------------------------------------------------------*/
static LSERR InitCurLine(PLSC plsc, LSCP cpFirst)
{
PLSLINE plsline = plsc->plslineCur;
memset(plsline, 0, cbRep(struct lsline, rgplnobj, plsc->lsiobjcontext.iobjMac));
plsline->tag = tagLSLINE;
plsline->lssubl.tag = tagLSSUBL;
plsline->lssubl.plsc = plsc;
plsline->lssubl.cpFirst = cpFirst;
/* reuse quick heap for dnodes if it possible */
if (plsc->pqhAllDNodesRecycled != NULL)
{
plsline->pqhAllDNodes = plsc->pqhAllDNodesRecycled;
plsc->pqhAllDNodesRecycled = NULL;
}
else
{
plsline->pqhAllDNodes = CreateQuickHeap(plsc, limAllDNodes,
sizeof (struct lsdnode), fTrue);
if (plsline->pqhAllDNodes == NULL )
return lserrOutOfMemory;
}
plsline->lssubl.fDupInvalid = fTrue;
plsline->lssubl.fContiguous = fTrue;
plsline->lssubl.plschunkcontext = &(plsc->lschunkcontextStorage);
plsline->lssubl.fMain = fTrue;
TurnOnAllSimpleText(plsc);
plsline->lslinfo.nDepthFormatLineMax = 1;
TurnOffLineCompressed(plsc);
TurnOffNominalToIdealEncounted(plsc);
TurnOffForeignObjectEncounted(plsc);
TurnOffTabEncounted(plsc);
TurnOffNonLeftTabEncounted(plsc);
TurnOffSubmittedSublineEncounted(plsc);
TurnOffAutodecimalTabPresent(plsc);
plsc->fHyphenated = fFalse;
plsc->fAdvanceBack = fFalse;
/* we use memset to set default value, below we check that after memset we really have
correct defaults */
Assert(plsline->lssubl.plsdnFirst == NULL);
Assert(plsline->lssubl.urColumnMax == 0);
Assert(plsline->lssubl.cpLim == 0);
Assert(plsline->lssubl.plsdnLast == NULL);
Assert(plsline->lssubl.urCur == 0);
Assert(plsline->lssubl.vrCur == 0);
Assert(plsline->lssubl.fAcceptedForDisplay == fFalse);
Assert(plsline->lssubl.fRightMarginExceeded == fFalse);
Assert(plsline->lssubl.plsdnUpTemp == NULL);
Assert(plsline->lssubl.pbrkcontext == NULL);
Assert(plsline->lslinfo.dvpAscent == 0); /* lslinfo */
Assert(plsline->lslinfo.dvrAscent == 0);
Assert(plsline->lslinfo.dvpDescent == 0);
Assert(plsline->lslinfo.dvrDescent == 0);
Assert(plsline->lslinfo.dvpMultiLineHeight == 0);
Assert(plsline->lslinfo.dvrMultiLineHeight == 0);
Assert(plsline->lslinfo.dvpAscentAutoNumber == 0);
Assert(plsline->lslinfo.dvrAscentAutoNumber == 0);
Assert(plsline->lslinfo.dvpDescentAutoNumber == 0);
Assert(plsline->lslinfo.dvrDescentAutoNumber == 0);
Assert(plsline->lslinfo.cpLim == 0);
Assert(plsline->lslinfo.dcpDepend == 0);
Assert(plsline->lslinfo.cpFirstVis == 0);
Assert(plsline->lslinfo.endr == endrNormal);
Assert(plsline->lslinfo.fAdvanced == fFalse);
Assert(plsline->lslinfo.vaAdvance == 0);
Assert(plsline->lslinfo.fFirstLineInPara == fFalse);
Assert(plsline->lslinfo.EffectsFlags == 0);
Assert(plsline->lslinfo.fTabInMarginExLine == fFalse);
Assert(plsline->lslinfo.fForcedBreak == fFalse);
Assert(plsline->upStartAutonumberingText == 0);
Assert(plsline->upLimAutonumberingText == 0);
Assert(plsline->upStartMainText == 0);
Assert(plsline->upLimLine == 0);
Assert(plsline->dvpAbove == 0);
Assert(plsline->dvpBelow == 0);
Assert(plsline->upRightMarginJustify == 0);
Assert(plsline->upLimUnderline == 0);
Assert(plsline->kspl == ksplNone);
Assert(!plsline->fCollectVisual);
Assert(!plsline->fNonRealDnodeEncounted);
Assert(!plsline->fNonZeroDvpPosEncounted);
Assert(plsline->AggregatedDisplayFlags == 0);
Assert(plsline->pad == 0);
#ifdef DEBUG
{
DWORD i;
for (i=0; i < plsc->lsiobjcontext.iobjMac; i++)
{
Assert(plsline->rgplnobj[i] == NULL);
}
}
#endif
return lserrNone;
}
/*----------------------------------------------------------------------------
/* C A N N O T C R E A T E L I N E */
/*----------------------------------------------------------------------------
%%Function: CannotCreateLine
%%Contact: igorzv
Parameters:
pplsline - (IN) ponter to a line structure to be deleted
lserr - (IN) code of an error
Called when LsCreateLine needs to return for an error condition.
----------------------------------------------------------------------------*/
static LSERR CannotCreateLine(PLSLINE* pplsline, LSERR lserr)
{
LSERR lserrIgnore;
PLSLINE plsline = *pplsline;
PLSC plsc = plsline->lssubl.plsc;
plsc->plslineCur = NULL;
plsc->lsstate = LsStateFree; /* we need free context to destroy line */
lserrIgnore = LsDestroyLine(plsc, plsline);
*pplsline = NULL;
return lserr;
}
/*----------------------------------------------------------------------------
/* E R R R E L E A S E P R E F E T C H E D R U N */
/*----------------------------------------------------------------------------
%%Function: ErrReleasePreFetchedRun
%%Contact: igorzv
Parameters:
plsc - (IN) ptr to line services context
plsrun - (IN) ponter to a run structure to be deleted
lserr - (IN) code of an error
Called in a error situation when fist run of main text has been prefetched .
----------------------------------------------------------------------------*/
static LSERR ErrReleasePreFetchedRun(PLSC plsc, PLSRUN plsrun, LSERR lserr)
{
LSERR lserrIgnore;
if (!plsc->fDontReleaseRuns)
lserrIgnore = plsc->lscbk.pfnReleaseRun(plsc->pols, plsrun);
return lserr;
}
/* R E M O V E L I N E O B J E C T S */
/*----------------------------------------------------------------------------
%%Function: RemoveLineObjects
%%Contact: igorzv
Parameter:
plsc - (IN) ponter to a line structure
Removes a line context of installed objects from an line.
----------------------------------------------------------------------------*/
LSERR RemoveLineObjects(PLSLINE plsline)
{
DWORD iobjMac;
PLSC plsc;
LSERR lserr, lserrFinal = lserrNone;
DWORD iobj;
PLNOBJ plnobj;
Assert(FIsLSLINE(plsline));
plsc = plsline->lssubl.plsc;
Assert(FIsLSC(plsc));
Assert(plsc->lsstate == LsStateDestroyingLine);
iobjMac = plsc->lsiobjcontext.iobjMac;
for (iobj = 0; iobj < iobjMac; iobj++)
{
plnobj = plsline->rgplnobj[iobj];
if (plnobj != NULL)
{
lserr = plsc->lsiobjcontext.rgobj[iobj].lsim.pfnDestroyLNObj(plnobj);
plsline->rgplnobj[iobj] = NULL;
if (lserr != lserrNone)
lserrFinal = lserr;
}
}
return lserrFinal;
}
#ifdef DEBUG
/* F R O U N D I N G O K */
/*----------------------------------------------------------------------------
%%Function: FRoundingOK
%%Contact: lenoxb
Checks for correctness of rounding algorithm in converting absolute to
device units, to agree with Word 6.0.
Checks that:
0.5 rounds up to 1.0,
1.4 rounds down to 1.4,
-0.5 rounds down to -1.0, and
-1.4 rounds up to -1.0.
----------------------------------------------------------------------------*/
static BOOL FRoundingOK(void)
{
LSDEVRES devresT;
Assert((czaUnitInch % 10) == 0);
devresT.dxpInch = czaUnitInch / 10;
if (UpFromUa(lstflowDefault, &devresT, 5) != 1)
return fFalse;
if (UpFromUa(lstflowDefault, &devresT, 14) != 1)
return fFalse;
if (UpFromUa(lstflowDefault, &devresT, -5) != -1)
return fFalse;
if (UpFromUa(lstflowDefault, &devresT, -14) != -1)
return fFalse;
return fTrue;
}
#endif /* DEBUG */