1855 lines
54 KiB
C
1855 lines
54 KiB
C
#include "lsidefs.h"
|
|
#include "dninfo.h"
|
|
#include "dnutils.h"
|
|
#include "fmti.h"
|
|
#include "getfmtst.h"
|
|
#include "iobj.h"
|
|
#include "iobjln.h"
|
|
#include "lsc.h"
|
|
#include "lsdnode.h"
|
|
#include "lsfetch.h"
|
|
#include "lsfrun.h"
|
|
#include "lsline.h"
|
|
#include "lsesc.h"
|
|
#include "lstext.h"
|
|
#include "ntiman.h"
|
|
#include "qheap.h"
|
|
#include "setfmtst.h"
|
|
#include "tabutils.h"
|
|
#include "zqfromza.h"
|
|
#include "lssubl.h"
|
|
#include "autonum.h"
|
|
#include "lscfmtfl.h"
|
|
|
|
#include <limits.h>
|
|
#include "lsmem.h" /* memset() */
|
|
|
|
/* L I M R G */
|
|
/*----------------------------------------------------------------------------
|
|
%%Function: LimRg
|
|
%%Contact: igorzv
|
|
|
|
Returns # of elements in an array.
|
|
----------------------------------------------------------------------------*/
|
|
#define LimRg(rg) (sizeof(rg)/sizeof((rg)[0]))
|
|
|
|
|
|
|
|
/* A S S E R T V A L I D F M T R E S */
|
|
/*----------------------------------------------------------------------------
|
|
%%Macro: AssertValidFmtres
|
|
%%Contact: lenoxb
|
|
|
|
Verifies that fmtrCk has a legal value.
|
|
----------------------------------------------------------------------------*/
|
|
#define AssertValidFmtres(fmtrCk) \
|
|
Assert( \
|
|
(fmtrCk) == fmtrCompletedRun || \
|
|
(fmtrCk) == fmtrExceededMargin || \
|
|
(fmtrCk) == fmtrTab || \
|
|
(fmtrCk) == fmtrStopped \
|
|
);
|
|
|
|
|
|
|
|
|
|
/* S E T T O M A X */
|
|
/*----------------------------------------------------------------------------
|
|
%%Macro: SetToMax
|
|
%%Contact: lenoxb
|
|
|
|
Sets "a" to the maximum of "a" and "b".
|
|
----------------------------------------------------------------------------*/
|
|
#define SetToMax(a,b) if ((a) < (b)) (a) = (b); else
|
|
|
|
|
|
|
|
|
|
/* S E T H E I G H T T O M A X */
|
|
/*----------------------------------------------------------------------------
|
|
%%Macro: SetHeightToMax
|
|
%%Contact: igorzv
|
|
|
|
Sets the line height elements of an LSLINFO structure to the maximum
|
|
of their current value, and the height of an arbitrary object.
|
|
(plslinfo)->dvrMultiLineHeight == dvHeightIgnore is sign not
|
|
to take into account this dnode
|
|
----------------------------------------------------------------------------*/
|
|
#define SetHeightToMax(plslinfo,pobjdim) \
|
|
{\
|
|
if ((pobjdim)->heightsRef.dvMultiLineHeight != dvHeightIgnore)\
|
|
{\
|
|
SetToMax((plslinfo)->dvrAscent, (pobjdim)->heightsRef.dvAscent);\
|
|
SetToMax((plslinfo)->dvpAscent, (pobjdim)->heightsPres.dvAscent);\
|
|
SetToMax((plslinfo)->dvrDescent, (pobjdim)->heightsRef.dvDescent);\
|
|
SetToMax((plslinfo)->dvpDescent, (pobjdim)->heightsPres.dvDescent);\
|
|
SetToMax((plslinfo)->dvpMultiLineHeight, (pobjdim)->heightsPres.dvMultiLineHeight);\
|
|
SetToMax((plslinfo)->dvrMultiLineHeight, (pobjdim)->heightsRef.dvMultiLineHeight);\
|
|
}\
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define PlnobjFromLsc(plsc,iobj) ((Assert(FIsLSC(plsc)), PlnobjFromLsline((plsc)->plslineCur,iobj)))
|
|
|
|
#define CreateLNObjInLsc(plsc, iobj) ((PLsimFromLsc(&((plsc)->lsiobjcontext),iobj))->pfnCreateLNObj\
|
|
(PilsobjFromLsc(&((plsc)->lsiobjcontext),iobj), \
|
|
&((plsc)->plslineCur->rgplnobj[iobj])))
|
|
|
|
/* This macros created to avoid code duplication */
|
|
|
|
#define FRunIsNotSimple(plschp, fHidden) \
|
|
(((plschp)->idObj != idObjTextChp) || \
|
|
((fHidden)) || \
|
|
((plschp)->fBorder) || \
|
|
FApplyNominalToIdeal(plschp))
|
|
|
|
#define CreateDnode(plsc, plsdnNew) \
|
|
(plsdnNew) = PvNewQuick(GetPqhAllDNodes(plsc), sizeof *(plsdnNew));\
|
|
if ((plsdnNew) == NULL)\
|
|
return lserrOutOfMemory;\
|
|
(plsdnNew)->tag = tagLSDNODE;\
|
|
(plsdnNew)->plsdnPrev = GetCurrentDnode(plsc);\
|
|
(plsdnNew)->plsdnNext = NULL;\
|
|
(plsdnNew)->plssubl = GetCurrentSubline(plsc);\
|
|
/* we don't connect dnode list with this dnode untill handler calls*/ \
|
|
/*Finish API, but we put correct pointer to previous in this dnode,*/ \
|
|
/*so we can easily link list in Finish routines */\
|
|
(plsdnNew)->cpFirst = GetCurrentCpLim(plsc); \
|
|
/* flush all flags, bellow check that result is what we expect */ \
|
|
*((DWORD *) ((&(plsdnNew)->dcp)+1)) = 0;\
|
|
Assert((plsdnNew)->klsdn == klsdnReal);\
|
|
Assert((plsdnNew)->fRigidDup == fFalse);\
|
|
Assert((plsdnNew)->fAdvancedPen == fFalse);\
|
|
Assert((plsdnNew)->fTab == fFalse);\
|
|
Assert((plsdnNew)->icaltbd == 0);\
|
|
Assert((plsdnNew)->fBorderNode == fFalse);\
|
|
Assert((plsdnNew)->fOpenBorder == fFalse);\
|
|
Assert((plsdnNew)->fEndOfSection == fFalse); \
|
|
Assert((plsdnNew)->fEndOfColumn == fFalse); \
|
|
Assert((plsdnNew)->fEndOfPage == fFalse); \
|
|
Assert((plsdnNew)->fEndOfPara == fFalse); \
|
|
Assert((plsdnNew)->fAltEndOfPara == fFalse); \
|
|
Assert((plsdnNew)->fSoftCR == fFalse); \
|
|
Assert((plsdnNew)->fInsideBorder == fFalse); \
|
|
Assert((plsdnNew)->fAutoDecTab == fFalse); \
|
|
Assert((plsdnNew)->fTabForAutonumber == fFalse);
|
|
|
|
|
|
#define FillRealPart(plsdnNew, plsfrunOfDnode)\
|
|
/* we don't initialize here variables that will be set in FiniSimpleRegular */ \
|
|
(plsdnNew)->u.real.pinfosubl = NULL;\
|
|
/* next two assignement we do to use DestroyDnodeList in the case of error */ \
|
|
(plsdnNew)->u.real.plsrun = (plsfrunOfDnode)->plsrun;\
|
|
(plsdnNew)->u.real.pdobj = NULL;\
|
|
/* we put amount of characters to dcp to check it in LsdnFinishSimpleByOneChar */ \
|
|
(plsdnNew)->dcp = (plsfrunOfDnode)->cwchRun; \
|
|
(plsdnNew)->cpLimOriginal = (plsdnNew)->cpFirst + (plsdnNew)->dcp;
|
|
|
|
#define CreateRealDnode(plsc,plsdnNew, plsrun)\
|
|
CreateDnode((plsc), (plsdnNew));\
|
|
FillRealPart((plsdnNew), (plsrun));
|
|
|
|
#define CreatePenDnode(plsc,plsdnNew)\
|
|
CreateDnode((plsc), (plsdnNew));\
|
|
(plsdnNew)->dcp = 0;\
|
|
(plsdnNew)->cpLimOriginal = (plsdnNew)->cpFirst;\
|
|
(plsdnNew)->u.pen.dur = 0;\
|
|
(plsdnNew)->u.pen.dup = 0;\
|
|
(plsdnNew)->u.pen.dvr = 0;\
|
|
(plsdnNew)->u.pen.dvp = 0;\
|
|
(plsdnNew)->klsdn = klsdnPenBorder;
|
|
|
|
#define CreateBorderDnode(plsc,plsdnNew, durBorder, dupBorder)\
|
|
CreateDnode((plsc), (plsdnNew));\
|
|
(plsdnNew)->dcp = 0;\
|
|
(plsdnNew)->cpLimOriginal = (plsdnNew)->cpFirst;\
|
|
(plsdnNew)->u.pen.dur = (durBorder);\
|
|
(plsdnNew)->u.pen.dup = (dupBorder);\
|
|
(plsdnNew)->u.pen.dvr = 0;\
|
|
(plsdnNew)->u.pen.dvp = 0;\
|
|
(plsdnNew)->klsdn = klsdnPenBorder; \
|
|
(plsdnNew)->fBorderNode = fTrue; \
|
|
TurnOnNonRealDnodeEncounted(plsc);
|
|
|
|
#define FNeedToCutPossibleContextViolation(plsc, plsdn) \
|
|
(FIsDnodeReal(plsdn) && \
|
|
((plsdn)->u.real.lschp.dcpMaxContext > 1) && \
|
|
(IdObjFromDnode(plsdn) == IobjTextFromLsc(&((plsc)->lsiobjcontext))) \
|
|
)
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
static LSERR CheckNewPara(PLSC, LSCP, LSCP, BOOL*);
|
|
static BOOL FLimitRunEsc(LSFRUN*, const LSESC*, DWORD);
|
|
static LSERR CreateInitialPen(PLSC plsc, long dur);
|
|
static LSERR UndoLastDnode(PLSC); /* IN: ls context */
|
|
static LSERR OpenBorder(PLSC plsc, PLSRUN plsrun);
|
|
static LSERR HandleSplat(PLSC plsc, FMTRES* pfmtres);
|
|
static LSERR ErrReleaseRunToFormat (PLSC, /* IN: ptr to line services context */
|
|
PLSRUN, /* IN: ponter to a run structure to be deleted */
|
|
LSERR); /* IN: code of an error */
|
|
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
/* F E T C H A P P E N D E S C R E S U M E C O R E */
|
|
/*----------------------------------------------------------------------------
|
|
%%Function: FetchAppendEscResumeCore
|
|
%%Contact: igorzv
|
|
Parameters:
|
|
plsc - (IN) ptr to line services context
|
|
urColumnMax - (IN) right margin where to stop
|
|
plsesc - (IN) escape characters
|
|
clsesc - (IN) # of escape characters
|
|
rgbreakrec - (IN) input array of break records
|
|
cbreakrec, (IN) number of records in input array
|
|
pfmtres - (OUT) result of last formatter
|
|
pcpLim - (OUT) where we stop fetching
|
|
pplsdnFirst - (OUT) first dnode that was created
|
|
pplsdnLast - (OUT) last dnode that was created
|
|
pur - (OUT) pen position after procedure
|
|
|
|
If cbreakrec > 0 fetches run with cpFirst from first break record.
|
|
After that if rigth msrgin is not exceeded cals FetchAppendEscCore
|
|
----------------------------------------------------------------------------*/
|
|
LSERR FetchAppendEscResumeCore(PLSC plsc, long urColumnMax, const LSESC* plsesc,
|
|
DWORD clsesc, const BREAKREC* rgbreakrec,
|
|
DWORD cbreakrec, FMTRES* pfmtres, LSCP* pcpLim,
|
|
PLSDNODE* pplsdnFirst, PLSDNODE* pplsdnLast,
|
|
long* pur)
|
|
|
|
{
|
|
LSFRUN lsfrun;
|
|
LSCHP lschp; /* local memory to store lschp */
|
|
BOOL fHidden;
|
|
FMTRES fmtresResume;
|
|
LSERR lserr;
|
|
PLSDNODE* pplsdnFirstStore; /* where to find plsdnFirst */
|
|
|
|
Assert(FIsLSC(plsc));
|
|
Assert(FFormattingAllowed(plsc));
|
|
Assert(!(rgbreakrec == NULL && cbreakrec != 0));
|
|
Assert(GetCurrentDnode(plsc) == NULL); /* it should be begining of a subline */
|
|
|
|
if (cbreakrec > 0)
|
|
{
|
|
/*Initialization; */
|
|
|
|
lsfrun.plschp = &lschp;
|
|
pplsdnFirstStore = GetWhereToPutLink(plsc, GetCurrentDnode(plsc));
|
|
|
|
/* fetch run that starts object to resume */
|
|
lserr = plsc->lscbk.pfnFetchRun(plsc->pols, rgbreakrec[0].cpFirst,
|
|
&lsfrun.lpwchRun, &lsfrun.cwchRun,
|
|
&fHidden, &lschp, &lsfrun.plsrun);
|
|
if (lserr != lserrNone)
|
|
return lserr;
|
|
|
|
if (lsfrun.cwchRun <= 0 || fHidden || lsfrun.plschp->idObj != rgbreakrec[0].idobj)
|
|
{
|
|
lserr = lserrInvalidBreakRecord;
|
|
if (!plsc->fDontReleaseRuns)
|
|
{
|
|
plsc->lscbk.pfnReleaseRun(plsc->pols, lsfrun.plsrun);
|
|
}
|
|
return lserr;
|
|
}
|
|
|
|
/* zero amount of characters before dispatching to an object */
|
|
lsfrun.cwchRun = 0;
|
|
|
|
lserr = ProcessOneRun(plsc, urColumnMax, &lsfrun, rgbreakrec,
|
|
cbreakrec,&fmtresResume);
|
|
if (lserr != lserrNone)
|
|
return lserr;
|
|
|
|
/* we know that resumed object is not text, so only two fmtres are possible
|
|
and we don't consider others */
|
|
Assert(fmtresResume == fmtrCompletedRun || fmtresResume == fmtrExceededMargin);
|
|
|
|
if (fmtresResume == fmtrCompletedRun)
|
|
{
|
|
|
|
lserr = FetchAppendEscCore(plsc, urColumnMax, plsesc, clsesc, pfmtres, pcpLim,
|
|
pplsdnFirst, pplsdnLast, pur);
|
|
if (lserr != lserrNone)
|
|
return lserr;
|
|
|
|
/* special handling of empty dnode list as an result of FetchAppendEscCore */
|
|
if (*pplsdnFirst == NULL)
|
|
{
|
|
*pplsdnLast = GetCurrentDnode(plsc); /* this assigning is correct even when
|
|
resumed object produces empty list
|
|
of dnodes because it starts subline */
|
|
|
|
*pfmtres = fmtresResume;
|
|
}
|
|
|
|
/* rewrite first dnode */
|
|
*pplsdnFirst = *pplsdnFirstStore;
|
|
}
|
|
else /* stop fetching here */
|
|
{
|
|
/* Prepare output */
|
|
*pfmtres = fmtresResume;
|
|
*pcpLim = GetCurrentCpLim(plsc);
|
|
*pplsdnFirst = *pplsdnFirstStore;
|
|
*pplsdnLast = GetCurrentDnode(plsc);
|
|
*pur = GetCurrentUr(plsc);
|
|
}
|
|
|
|
Assert((*pplsdnFirst == NULL) == (*pplsdnLast == NULL));
|
|
Assert((*pplsdnLast == NULL) || ((*pplsdnLast)->plsdnNext == NULL));
|
|
|
|
return lserrNone;
|
|
}
|
|
else /* no breakrecords */
|
|
{
|
|
return FetchAppendEscCore(plsc, urColumnMax, plsesc, clsesc, pfmtres, pcpLim,
|
|
pplsdnFirst, pplsdnLast, pur);
|
|
}
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
/* F E T C H A P P E N D E S C C O R E */
|
|
/*----------------------------------------------------------------------------
|
|
%%Function: FetchAppendEscCore
|
|
%%Contact: igorzv
|
|
Parameters:
|
|
plsc - (IN) ptr to line services context
|
|
urColumnMax - (IN) right margin where to stop
|
|
plsesc - (IN) escape characters
|
|
clsesc - (IN) # of escape characters
|
|
pfmtres - (OUT) result of last formatter
|
|
pcpLim - (OUT) where we stop fetching
|
|
pplsdnFirst - (OUT) first dnode that was created
|
|
pplsdnLast - (OUT) last dnode that was created
|
|
pur - (OUT) pen position after procedure
|
|
|
|
Loop: fetch run, dispatch it to object handler until escape character
|
|
or terminal fmtres.
|
|
----------------------------------------------------------------------------*/
|
|
|
|
|
|
LSERR FetchAppendEscCore(PLSC plsc, long urColumnMax, const LSESC* plsesc,
|
|
DWORD clsesc, FMTRES* pfmtres, LSCP* pcpLim,
|
|
PLSDNODE* pplsdnFirst, PLSDNODE* pplsdnLast,
|
|
long* pur)
|
|
|
|
{
|
|
|
|
BOOL fDone = fFalse;
|
|
LSFRUN lsfrun;
|
|
LSCHP lschp; /* local memory to store lschp */
|
|
FMTRES fmtres;
|
|
BOOL fHidden;
|
|
LSCP cpLimOfCutRun = (LSCP)(-1); /* cpLim of run that was cuted according with Esc character
|
|
is not valid in other cases
|
|
we use it to check that whole such run was handled by formater */
|
|
LSCP cpPrev = (LSCP)(-1); /* cp of previous run valid only after first iteration */
|
|
LSERR lserr;
|
|
PLSDNODE* pplsdnFirstStore; /* where to find plsdnFirst */
|
|
|
|
|
|
Assert(FIsLSC(plsc));
|
|
Assert(FFormattingAllowed(plsc));
|
|
|
|
/*Initialization; */
|
|
lsfrun.plschp = &lschp;
|
|
fmtres = fmtrCompletedRun; /* it will be output if return right away with esc character */
|
|
pplsdnFirstStore = GetWhereToPutLink(plsc, GetCurrentDnode(plsc));
|
|
|
|
while (!fDone)
|
|
{
|
|
cpPrev = GetCurrentCpLim(plsc);
|
|
/* FetchRun */
|
|
lserr = plsc->lscbk.pfnFetchRun(plsc->pols, GetCurrentCpLim(plsc),
|
|
&lsfrun.lpwchRun, &lsfrun.cwchRun,
|
|
&fHidden, &lschp, &lsfrun.plsrun);
|
|
if (lserr != lserrNone)
|
|
return lserr;
|
|
|
|
if (lsfrun.cwchRun <= 0)
|
|
{
|
|
lserr = lserrInvalidDcpFetched;
|
|
if (!plsc->fDontReleaseRuns)
|
|
{
|
|
plsc->lscbk.pfnReleaseRun(plsc->pols, lsfrun.plsrun);
|
|
}
|
|
return lserr;
|
|
}
|
|
|
|
if (fHidden)
|
|
{
|
|
AdvanceCurrentCpLim(plsc, lsfrun.cwchRun);
|
|
if (lsfrun.plsrun != NULL && !plsc->fDontReleaseRuns) /* we have not used this plsrun */
|
|
{
|
|
lserr = plsc->lscbk.pfnReleaseRun(plsc->pols, lsfrun.plsrun);
|
|
if (lserr != lserrNone)
|
|
return lserr;
|
|
}
|
|
/* Handle vanish end of paragraph; */
|
|
/* There is situation in Word (see bug 118) when after fetching hidden text
|
|
paragraph boundaries can be changed. So we have to call CheckNewPara
|
|
every time after hidden text */
|
|
|
|
lserr = CheckNewPara(plsc, cpPrev, GetCurrentCpLim(plsc), &fDone);
|
|
if (lserr != lserrNone)
|
|
return lserr;
|
|
|
|
if (fDone)
|
|
{
|
|
/* it will eventually force stop formatting so we should apply
|
|
nominal to ideal here */
|
|
if (FNominalToIdealEncounted(plsc))
|
|
{
|
|
lserr = ApplyNominalToIdeal(PlschunkcontextFromSubline(GetCurrentSubline(plsc)),
|
|
&plsc->lsiobjcontext,
|
|
plsc->grpfManager, plsc->lsadjustcontext.lskj,
|
|
FIsSubLineMain(GetCurrentSubline(plsc)),
|
|
FLineContainsAutoNumber(plsc),
|
|
GetCurrentDnode(plsc));
|
|
if (lserr != lserrNone)
|
|
return lserr;
|
|
}
|
|
fmtres = fmtrStopped;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Check Esc character; */
|
|
if (clsesc > 0 && FLimitRunEsc(&lsfrun, plsesc, clsesc))
|
|
{
|
|
cpLimOfCutRun = (LSCP) (GetCurrentCpLim(plsc) + lsfrun.cwchRun);
|
|
fDone = (lsfrun.cwchRun == 0);
|
|
}
|
|
|
|
if (!fDone)
|
|
{
|
|
lserr = ProcessOneRun(plsc, urColumnMax, &lsfrun, NULL, 0, &fmtres);
|
|
if (lserr != lserrNone)
|
|
return lserr;
|
|
|
|
/*Check fmtres: Are formating done?; */
|
|
switch (fmtres)
|
|
{
|
|
case fmtrCompletedRun:
|
|
fDone = (GetCurrentCpLim(plsc) == cpLimOfCutRun); /* is true only if we cuted because */
|
|
Assert(!fDone || clsesc > 0); /* of esc character and formater handled such*/
|
|
break; /* run completely */
|
|
|
|
case fmtrExceededMargin:
|
|
fDone = fTrue;
|
|
break;
|
|
|
|
case fmtrTab:
|
|
fDone = fTrue;
|
|
break;
|
|
|
|
case fmtrStopped:
|
|
fDone = fTrue;
|
|
break;
|
|
|
|
default:
|
|
NotReached();
|
|
|
|
}
|
|
|
|
}
|
|
else /* after limiting run by esc characters it was empty */
|
|
{
|
|
if (lsfrun.plsrun != NULL && !plsc->fDontReleaseRuns) /* we have not used this plsrun */
|
|
{
|
|
lserr = plsc->lscbk.pfnReleaseRun(plsc->pols, lsfrun.plsrun);
|
|
if (lserr != lserrNone)
|
|
return lserr;
|
|
fmtres = fmtrCompletedRun;
|
|
}
|
|
}
|
|
} /* if/else hidden */
|
|
}
|
|
|
|
|
|
/* Prepare output */
|
|
*pfmtres = fmtres;
|
|
*pcpLim = GetCurrentCpLim(plsc);
|
|
*pplsdnFirst = *pplsdnFirstStore;
|
|
if (*pplsdnFirst != NULL)
|
|
*pplsdnLast = GetCurrentDnode(plsc);
|
|
else
|
|
*pplsdnLast = NULL;
|
|
*pur = GetCurrentUr(plsc);
|
|
|
|
Assert((*pplsdnFirst == NULL) == (*pplsdnLast == NULL));
|
|
Assert((*pplsdnLast == NULL) || ((*pplsdnLast)->plsdnNext == NULL));
|
|
|
|
return lserrNone;
|
|
}
|
|
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
/* P R O C E S S O N E R U N */
|
|
/*----------------------------------------------------------------------------
|
|
%%Function: ProcessOneRun
|
|
%%Contact: igorzv
|
|
Parameters:
|
|
plsc - (IN) ptr to line services context
|
|
urColumnMax - (IN) right margin where to stop
|
|
plsfrun - (IN) given run
|
|
rgbreakrec - (IN) input array of break records
|
|
cbreakrec, (IN) number of records in input array
|
|
pfmtres - (OUT) result of formatting
|
|
|
|
1) If run it's not a text run applies nominal to ideal to previous text chunk.
|
|
To have correct pen position before dispatching to an foreign object.
|
|
2) Get text metrics and dispatches run to an handler.
|
|
3) If fmtres is terminal applies nominal to ideal to the last chunk.
|
|
----------------------------------------------------------------------------*/
|
|
|
|
|
|
LSERR ProcessOneRun (PLSC plsc, long urColumnMax, const LSFRUN* plsfrun,
|
|
const BREAKREC* rgbreakrec,
|
|
DWORD cbreakrec, FMTRES* pfmtres)
|
|
|
|
|
|
{
|
|
DWORD iobj;
|
|
LSIMETHODS* plsim;
|
|
PLNOBJ plnobj;
|
|
struct fmtin fmti;
|
|
LSERR lserr;
|
|
PLSDNODE plsdnNew;
|
|
PLSDNODE plsdnToFinishOld; /* we should restore it after every formater */
|
|
PLSSUBL plssublOld;
|
|
PLSDNODE plsdnNomimalToIdeal;
|
|
PLSDNODE* pplsdnToStoreNext;
|
|
PLSDNODE plsdnNext;
|
|
PLSDNODE plsdnCurrent;
|
|
PLSDNODE plsdnLast;
|
|
BOOL fInterruptBorder;
|
|
BOOL fInsideBorderUp = fFalse;
|
|
BOOL fBordered = fFalse;
|
|
|
|
|
|
|
|
Assert(FIsLSC(plsc));
|
|
Assert(!(rgbreakrec == NULL && cbreakrec != 0));
|
|
|
|
|
|
plsdnToFinishOld = GetDnodeToFinish(plsc);
|
|
plssublOld = GetCurrentSubline(plsc);
|
|
plsdnCurrent = GetCurrentDnode(plsc);
|
|
pplsdnToStoreNext = GetWhereToPutLink(plsc, plsdnCurrent);
|
|
if (plsdnToFinishOld != NULL)
|
|
fInsideBorderUp = plsdnToFinishOld->fInsideBorder;
|
|
|
|
|
|
if (plsfrun->plschp->idObj == idObjTextChp)
|
|
iobj = IobjTextFromLsc(&plsc->lsiobjcontext);
|
|
else
|
|
iobj = plsfrun->plschp->idObj;
|
|
|
|
Assert (FIobjValid(&plsc->lsiobjcontext, iobj)); /* Reject other out of range ids */
|
|
if (!FIobjValid(&plsc->lsiobjcontext, iobj)) /* for both debug and ship builds. */
|
|
return ErrReleaseRunToFormat(plsc, plsfrun->plsrun, lserrInvalidObjectIdFetched);
|
|
|
|
/* here we are catching for situatuion when client adding text dnode to a chunk to
|
|
which nominal to ideal has been applied, such situation will lead later to applying nominal
|
|
to ideal twice to the same dnode, and this text doesn't like */
|
|
AssertImplies(iobj == IobjTextFromLsc(&plsc->lsiobjcontext),
|
|
!FNTIAppliedToLastChunk(PlschunkcontextFromSubline(plssublOld)));
|
|
if (iobj == IobjTextFromLsc(&plsc->lsiobjcontext) &&
|
|
FNTIAppliedToLastChunk(PlschunkcontextFromSubline(plssublOld)))
|
|
return ErrReleaseRunToFormat(plsc, plsfrun->plsrun, lserrFormattingFunctionDisabled);
|
|
|
|
|
|
plsim = PLsimFromLsc(&plsc->lsiobjcontext, iobj);
|
|
|
|
if (iobj != IobjTextFromLsc(&plsc->lsiobjcontext))
|
|
{
|
|
TurnOffAllSimpleText(plsc); /* not text */
|
|
TurnOnForeignObjectEncounted(plsc);
|
|
|
|
if (FNominalToIdealEncounted(plsc))
|
|
{
|
|
lserr = ApplyNominalToIdeal(PlschunkcontextFromSubline(plssublOld), &plsc->lsiobjcontext,
|
|
plsc->grpfManager, plsc->lsadjustcontext.lskj,
|
|
FIsSubLineMain(plssublOld), FLineContainsAutoNumber(plsc),
|
|
plsdnCurrent);
|
|
if (lserr != lserrNone)
|
|
return ErrReleaseRunToFormat(plsc, plsfrun->plsrun, lserr);
|
|
|
|
/* we should recalculate plsdnCurrent because nominal to ideal can destroy last dnode */
|
|
plsdnCurrent = GetCurrentDnode(plsc);
|
|
pplsdnToStoreNext = GetWhereToPutLink(plsc, plsdnCurrent);
|
|
}
|
|
|
|
}
|
|
|
|
FlushNTIAppliedToLastChunk(PlschunkcontextFromSubline(plssublOld));
|
|
|
|
/* creating border dnodes */
|
|
/* skip back pen dnodes */
|
|
while (plsdnCurrent != NULL && FIsDnodePen(plsdnCurrent))
|
|
{
|
|
plsdnCurrent = plsdnCurrent->plsdnPrev;
|
|
}
|
|
|
|
if (FDnodeHasBorder(plsdnCurrent) &&
|
|
!(FIsDnodeBorder(plsdnCurrent) && !FIsDnodeOpenBorder(plsdnCurrent))) /* previous dnode has unclosed border */
|
|
/* condition in if looks superfluous but it works correctly even if dnodes deleting
|
|
happend during formatting */
|
|
{
|
|
if (plsfrun->plschp->fBorder)
|
|
{
|
|
/* check that client wants to border runs together */
|
|
lserr = plsc->lscbk.pfnFInterruptBorder(plsc->pols, plsdnCurrent->u.real.plsrun,
|
|
plsfrun->plsrun, &fInterruptBorder);
|
|
if (lserr != lserrNone)
|
|
return ErrReleaseRunToFormat(plsc, plsfrun->plsrun, lserr);
|
|
|
|
if (fInterruptBorder)
|
|
{
|
|
/* close previous border and open new one */
|
|
lserr = CloseCurrentBorder(plsc);
|
|
if (lserr != lserrNone)
|
|
return ErrReleaseRunToFormat(plsc, plsfrun->plsrun, lserr);
|
|
lserr = OpenBorder(plsc, plsfrun->plsrun);
|
|
if (lserr != lserrNone)
|
|
return ErrReleaseRunToFormat(plsc, plsfrun->plsrun, lserr);
|
|
}
|
|
fBordered = fTrue;
|
|
}
|
|
else
|
|
{
|
|
lserr = CloseCurrentBorder(plsc);
|
|
if (lserr != lserrNone)
|
|
return ErrReleaseRunToFormat(plsc, plsfrun->plsrun, lserr);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (plsfrun->plschp->fBorder)
|
|
{
|
|
if (fInsideBorderUp)
|
|
{
|
|
/* border is open on upper level: turn off border flag */
|
|
((PLSCHP) (plsfrun->plschp))->fBorder = fFalse;
|
|
}
|
|
else
|
|
{
|
|
lserr = OpenBorder(plsc, plsfrun->plsrun);
|
|
if (lserr != lserrNone)
|
|
return ErrReleaseRunToFormat(plsc, plsfrun->plsrun, lserr);
|
|
fBordered = fTrue;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* we always create real dnode and change it for pen if needed in Finish method */
|
|
CreateRealDnode(plsc, plsdnNew, plsfrun);
|
|
plsdnNew->fInsideBorder = fInsideBorderUp || fBordered;
|
|
|
|
/* initialization of fmti */
|
|
|
|
|
|
fmti.lsfgi.fFirstOnLine = FIsFirstOnLine(plsdnNew) && FIsSubLineMain(plssublOld);
|
|
fmti.lsfgi.cpFirst = GetCurrentCpLim(plsc);
|
|
fmti.lsfgi.urPen = GetCurrentUr(plsc);
|
|
fmti.lsfgi.vrPen = GetCurrentVr(plsc);
|
|
|
|
fmti.lsfgi.urColumnMax = urColumnMax;
|
|
|
|
fmti.lsfgi.lstflow = plssublOld->lstflow;
|
|
fmti.lsfrun = *plsfrun;
|
|
fmti.plsdnTop = plsdnNew;
|
|
|
|
|
|
lserr = plsc->lscbk.pfnGetRunTextMetrics(plsc->pols, fmti.lsfrun.plsrun,
|
|
lsdevReference, fmti.lsfgi.lstflow, &fmti.lstxmRef);
|
|
if (lserr != lserrNone)
|
|
{
|
|
DestroyDnodeList (&plsc->lscbk, plsc->pols, &plsc->lsiobjcontext,
|
|
plsdnNew, plsc->fDontReleaseRuns);
|
|
return lserr;
|
|
}
|
|
|
|
if (plsc->lsdocinf.fPresEqualRef)
|
|
fmti.lstxmPres = fmti.lstxmRef;
|
|
else
|
|
{
|
|
lserr = plsc->lscbk.pfnGetRunTextMetrics(plsc->pols, fmti.lsfrun.plsrun,
|
|
lsdevPres, fmti.lsfgi.lstflow,
|
|
&fmti.lstxmPres);
|
|
if (lserr != lserrNone)
|
|
{
|
|
DestroyDnodeList (&plsc->lscbk, plsc->pols, &plsc->lsiobjcontext,
|
|
plsdnNew, plsc->fDontReleaseRuns);
|
|
return lserr;
|
|
}
|
|
}
|
|
|
|
|
|
plnobj = PlnobjFromLsc(plsc, iobj);
|
|
|
|
|
|
if (plnobj == NULL)
|
|
{
|
|
lserr = CreateLNObjInLsc(plsc, iobj);
|
|
if (lserr != lserrNone)
|
|
{
|
|
DestroyDnodeList (&plsc->lscbk, plsc->pols, &plsc->lsiobjcontext,
|
|
plsdnNew, plsc->fDontReleaseRuns);
|
|
return lserr;
|
|
}
|
|
plnobj = PlnobjFromLsc(plsc, iobj);
|
|
}
|
|
|
|
/* set dnode to finish */
|
|
SetDnodeToFinish(plsc, plsdnNew);
|
|
/* set current subline to NULL */
|
|
SetCurrentSubline(plsc, NULL);
|
|
|
|
if (cbreakrec == 0)
|
|
{
|
|
lserr = plsim->pfnFmt(plnobj, &fmti, pfmtres);
|
|
}
|
|
else{
|
|
if (plsim->pfnFmtResume == NULL)
|
|
return lserrInvalidBreakRecord;
|
|
lserr = plsim->pfnFmtResume(plnobj, rgbreakrec, cbreakrec, &fmti, pfmtres);
|
|
}
|
|
|
|
if (lserr != lserrNone)
|
|
{
|
|
if (plsc->lslistcontext.plsdnToFinish != NULL) /* dnode hasn't added to list */
|
|
DestroyDnodeList (&plsc->lscbk, plsc->pols, &plsc->lsiobjcontext,
|
|
plsdnNew, plsc->fDontReleaseRuns);
|
|
/* we should restore dnode to finish and current subline to properly handle
|
|
error on upper level */
|
|
SetCurrentSubline(plsc, plssublOld);
|
|
SetDnodeToFinish(plsc, plsdnToFinishOld);
|
|
return lserr;
|
|
}
|
|
|
|
AssertValidFmtres(*pfmtres);
|
|
|
|
if (GetCurrentSubline(plsc) != NULL || GetDnodeToFinish(plsc) != NULL)
|
|
{
|
|
/* we should restore dnode to finish and current subline to properly handle
|
|
error on upper level */
|
|
SetCurrentSubline(plsc, plssublOld);
|
|
SetDnodeToFinish(plsc, plsdnToFinishOld);
|
|
return lserrUnfinishedDnode;
|
|
}
|
|
|
|
/* restore dnode to finish and current subline */
|
|
SetCurrentSubline(plsc, plssublOld);
|
|
SetDnodeToFinish(plsc, plsdnToFinishOld);
|
|
|
|
/* to avoid all problems with deleteing dnodes we don't use plsdnNew */
|
|
plsdnLast = GetCurrentDnodeSubl(plssublOld);
|
|
|
|
/* case of tab */
|
|
if (*pfmtres == fmtrTab)
|
|
{
|
|
plsdnLast->fTab = fTrue;
|
|
/* caller later can skip this tab so we prepare zero values */
|
|
Assert(FIsDnodeReal(plsdnLast));
|
|
Assert(IdObjFromDnode(plsdnLast) == IobjTextFromLsc(&plsc->lsiobjcontext));
|
|
|
|
TurnOffAllSimpleText(plsc); /* not text */
|
|
}
|
|
|
|
/* case of splat */
|
|
if (*pfmtres == fmtrStopped && plsdnLast != NULL && FIsDnodeSplat(plsdnLast))
|
|
{
|
|
lserr = HandleSplat(plsc, pfmtres);
|
|
if (lserr != lserrNone)
|
|
return lserr;
|
|
/* Handle splat can delete plsdnLast */
|
|
plsdnLast = GetCurrentDnodeSubl(plssublOld);
|
|
}
|
|
|
|
/* in a case of exceeded margin or hard break or tab (so all values of fmtres but fmtrCompletedRun) */
|
|
/* we need apply nominal to ideal to have correct lenght */
|
|
if (*pfmtres != fmtrCompletedRun && plsdnLast != NULL && FNominalToIdealEncounted(plsc))
|
|
{
|
|
if (*pfmtres == fmtrTab || FIsDnodeSplat(plsdnLast))
|
|
plsdnNomimalToIdeal = plsdnLast->plsdnPrev;
|
|
else
|
|
plsdnNomimalToIdeal = plsdnLast;
|
|
|
|
lserr = ApplyNominalToIdeal(PlschunkcontextFromSubline(plssublOld), &plsc->lsiobjcontext,
|
|
plsc->grpfManager, plsc->lsadjustcontext.lskj,
|
|
FIsSubLineMain(plssublOld), FLineContainsAutoNumber(plsc),
|
|
plsdnNomimalToIdeal);
|
|
if (lserr != lserrNone)
|
|
return lserr;
|
|
|
|
/* ApplyNominalToIdeal can delete plsdnLast */
|
|
plsdnLast = GetCurrentDnodeSubl(plssublOld);
|
|
/* if we run nominal to ideal because of tab chunk of text to which
|
|
nominal to ideal is applied is not last chunk */
|
|
if (*pfmtres == fmtrTab || FIsDnodeSplat(plsdnLast))
|
|
FlushNTIAppliedToLastChunk(PlschunkcontextFromSubline(plssublOld));
|
|
|
|
/* in a case of exceeded right margin we should extract dcpMaxContext characters
|
|
because after fetching further result of nominal to ideal can be different for these
|
|
characters: examples ligatures or kerning */
|
|
if (*pfmtres == fmtrExceededMargin &&
|
|
FNeedToCutPossibleContextViolation(plsc, plsdnLast))
|
|
{
|
|
lserr = CutPossibleContextViolation(PlschunkcontextFromSubline(plssublOld),
|
|
plsdnLast);
|
|
if (lserr != lserrNone)
|
|
return lserr;
|
|
/* such procedure also can delete plsdnLast */
|
|
plsdnLast = GetCurrentDnodeSubl(plssublOld);
|
|
}
|
|
|
|
}
|
|
|
|
if (iobj != IobjTextFromLsc(&plsc->lsiobjcontext))
|
|
/* only in this case there is a possibility to apply width modification
|
|
to preceding character */
|
|
{
|
|
/* we are actually applying width modification to preceding character if first
|
|
dnode produced by formating is non text */
|
|
/* we can't relly on plsdnLast here because of such Finish methods as
|
|
FinishByOneCharacter and FinishBySubline */
|
|
/* we still rely here on pplsdnToStoreNext in other words we assume that
|
|
plsdnCurrent (the current dnode in the begining of our procedure ) has not been
|
|
deleted during nominal to ideal. To prove this we use that nominal to ideal has been
|
|
already applied to plsdnCurrent*/
|
|
plsdnNext = *pplsdnToStoreNext;
|
|
Assert(plsdnNext == NULL || FIsLSDNODE(plsdnNext));
|
|
if (FNominalToIdealEncounted(plsc) &&
|
|
plsdnNext != NULL &&
|
|
FIsDnodeReal(plsdnNext) &&
|
|
IdObjFromDnode(plsdnNext) != IobjTextFromLsc(&plsc->lsiobjcontext)
|
|
)
|
|
{
|
|
lserr = ApplyModWidthToPrecedingChar(PlschunkcontextFromSubline(plssublOld),
|
|
&plsc->lsiobjcontext, plsc->grpfManager,
|
|
plsc->lsadjustcontext.lskj, plsdnNext);
|
|
if (lserr != lserrNone)
|
|
return lserr;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
return lserrNone;
|
|
}
|
|
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
/* Q U I C K F O R M A T T I N G */
|
|
/*----------------------------------------------------------------------------
|
|
%%Function: QuickFormatting
|
|
%%Contact: igorzv
|
|
Parameters:
|
|
plsc - (IN) ptr to line services context
|
|
plsfrun - (IN) given run
|
|
urColumnMax - (IN) right margin where to stop
|
|
pfGeneral - (OUT) quick formatting was stopped: we should use general formatting
|
|
pfHardStop - (OUT) formatting ended with hard break
|
|
pcpLim - (OUT) cpLim after procedure
|
|
pur - (OUT) pen position after procedure
|
|
|
|
Works only with text runs without nominal to ideal and without tabs.
|
|
Stops if condition below is broken.
|
|
----------------------------------------------------------------------------*/
|
|
|
|
|
|
LSERR QuickFormatting(PLSC plsc, LSFRUN* plsfrun, long urColumnMax,
|
|
BOOL* pfGeneral, BOOL* pfHardStop,
|
|
LSCP* pcpLim, long* pur)
|
|
|
|
{
|
|
|
|
struct fmtin fmti;
|
|
LSLINFO* plslinfoText;
|
|
DWORD iobjText;
|
|
PLNOBJ plnobjText;
|
|
PLSLINE plsline;
|
|
BOOL fHidden;
|
|
const POLS pols = plsc->pols;
|
|
BOOL fGeneral;
|
|
FMTRES fmtres = fmtrCompletedRun;
|
|
LSERR lserr;
|
|
PLSDNODE plsdnNew;
|
|
PLSSUBL plssubl;
|
|
|
|
iobjText = IobjTextFromLsc(&(plsc->lsiobjcontext));
|
|
plnobjText = PlnobjFromLsc(plsc, iobjText);
|
|
plssubl = GetCurrentSubline(plsc);
|
|
|
|
fmti.lsfrun = *plsfrun;
|
|
fmti.lsfgi.fFirstOnLine = TRUE;
|
|
fmti.lsfgi.cpFirst = GetCurrentCpLim(plsc);
|
|
fmti.lsfgi.vrPen = GetCurrentVr(plsc);
|
|
fmti.lsfgi.urPen = GetCurrentUr(plsc);
|
|
fmti.lsfgi.lstflow = plssubl->lstflow;
|
|
fmti.lsfgi.urColumnMax = urColumnMax;
|
|
|
|
|
|
plsline = plsc->plslineCur;
|
|
plslinfoText = &(plsline->lslinfo);
|
|
|
|
fGeneral = fFalse;
|
|
fHidden = fFalse; /* in InitTextParams we already skipped all vanished text */
|
|
|
|
|
|
|
|
for (;;) /* "break" exits quick-format loop */
|
|
{
|
|
/* Run has been alreary fetched */
|
|
|
|
/*we don't want to handle here vanished text, foreign object, nominal to ideal */
|
|
if ( FRunIsNotSimple(fmti.lsfrun.plschp, fHidden))
|
|
{
|
|
/* we should release run here, in general procedure we will fetch it again */
|
|
if (!plsc->fDontReleaseRuns)
|
|
{
|
|
|
|
lserr = plsc->lscbk.pfnReleaseRun(plsc->pols, fmti.lsfrun.plsrun);
|
|
if (lserr != lserrNone)
|
|
return lserr;
|
|
}
|
|
fGeneral = fTrue;
|
|
break;
|
|
}
|
|
|
|
/*Create dnode for text; */
|
|
CreateRealDnode(plsc, plsdnNew, &fmti.lsfrun);
|
|
|
|
SetDnodeToFinish(plsc, plsdnNew);
|
|
|
|
/* prepare fmtin */
|
|
fmti.plsdnTop = plsdnNew;
|
|
|
|
|
|
lserr = plsc->lscbk.pfnGetRunTextMetrics(pols, fmti.lsfrun.plsrun,
|
|
lsdevReference, fmti.lsfgi.lstflow, &fmti.lstxmRef);
|
|
if (lserr != lserrNone)
|
|
{
|
|
DestroyDnodeList (&plsc->lscbk, plsc->pols, &plsc->lsiobjcontext,
|
|
plsdnNew, plsc->fDontReleaseRuns);
|
|
return lserr;
|
|
}
|
|
|
|
if (plsc->lsdocinf.fPresEqualRef)
|
|
{
|
|
fmti.lstxmPres = fmti.lstxmRef;
|
|
}
|
|
else
|
|
{
|
|
lserr = plsc->lscbk.pfnGetRunTextMetrics(pols, fmti.lsfrun.plsrun,
|
|
lsdevPres, fmti.lsfgi.lstflow,
|
|
&fmti.lstxmPres);
|
|
if (lserr != lserrNone)
|
|
{
|
|
DestroyDnodeList (&plsc->lscbk, plsc->pols, &plsc->lsiobjcontext,
|
|
plsdnNew, plsc->fDontReleaseRuns);
|
|
return lserr;
|
|
}
|
|
}
|
|
|
|
SetCurrentSubline(plsc, NULL);
|
|
lserr = FmtText(plnobjText, &fmti, &fmtres);
|
|
if (lserr != lserrNone)
|
|
{
|
|
if (plsc->lslistcontext.plsdnToFinish != NULL) /* dnode hasn't added to list */
|
|
DestroyDnodeList (&plsc->lscbk, plsc->pols, &plsc->lsiobjcontext,
|
|
plsdnNew, plsc->fDontReleaseRuns);
|
|
return lserr;
|
|
}
|
|
/* restore current subline */
|
|
SetCurrentSubline(plsc, plssubl);
|
|
|
|
if (fmtres == fmtrTab ) /* tab: we quite from quick loop deleting this dnode
|
|
because we will append it again in FormatGeneralCase */
|
|
{
|
|
lserr = UndoLastDnode(plsc); /* dnode is already in list */
|
|
if (lserr != lserrNone)
|
|
return lserr;
|
|
fGeneral = fTrue;
|
|
break;
|
|
}
|
|
|
|
|
|
AssertValidFmtres(fmtres);
|
|
|
|
SetHeightToMax(plslinfoText, &(plsdnNew->u.real.objdim));
|
|
|
|
if (FIsDnodeSplat(plsdnNew))
|
|
{
|
|
lserr = HandleSplat(plsc, &fmtres);
|
|
if (lserr != lserrNone)
|
|
return lserr;
|
|
}
|
|
|
|
if (fmtres != fmtrCompletedRun)
|
|
{
|
|
/* after break we should check that final heights is not zero */
|
|
/* otherwise we take heights from last run */
|
|
/* so we will have correct line height after quick break */
|
|
if (plslinfoText->dvrAscent == 0 && plslinfoText->dvrDescent == 0)
|
|
{
|
|
plslinfoText->dvrAscent = fmti.lstxmRef.dvAscent;
|
|
plslinfoText->dvpAscent = fmti.lstxmPres.dvAscent;
|
|
plslinfoText->dvrDescent = fmti.lstxmRef.dvDescent;
|
|
plslinfoText->dvpDescent = fmti.lstxmPres.dvDescent;
|
|
plslinfoText->dvpMultiLineHeight = dvHeightIgnore;
|
|
plslinfoText->dvrMultiLineHeight = dvHeightIgnore;
|
|
}
|
|
break;
|
|
}
|
|
|
|
/* prepare next iteration; */
|
|
|
|
fmti.lsfgi.fFirstOnLine = fFalse;
|
|
fmti.lsfgi.urPen = GetCurrentUr(plsc);
|
|
fmti.lsfgi.cpFirst = GetCurrentCpLim(plsc);
|
|
|
|
lserr = plsc->lscbk.pfnFetchRun(pols, fmti.lsfgi.cpFirst,
|
|
&fmti.lsfrun.lpwchRun,
|
|
&fmti.lsfrun.cwchRun,
|
|
&fHidden, (LSCHP *)fmti.lsfrun.plschp,
|
|
&fmti.lsfrun.plsrun);
|
|
if (lserr != lserrNone)
|
|
return lserr;
|
|
|
|
Assert(fmti.lsfrun.cwchRun > 0);
|
|
|
|
} /* for (;;) */
|
|
|
|
|
|
|
|
/* prepare output */
|
|
*pfGeneral = fGeneral;
|
|
*pfHardStop = (fmtres == fmtrStopped);
|
|
*pcpLim = GetCurrentCpLim(plsc);
|
|
*pur = GetCurrentUr(plsc);
|
|
|
|
return lserrNone;
|
|
|
|
}
|
|
|
|
|
|
/* C H E C K N E W P A R A */
|
|
/*----------------------------------------------------------------------------
|
|
%%Function: CheckNewPara
|
|
%%Contact: igorzv
|
|
Parameters:
|
|
plsc - (IN) ptr to line services context
|
|
cpPrev - (IN) cp in old paragraph
|
|
cpThis - (IN) cp in new paragraph
|
|
pfQuit - (OUT) stop formatting because new paragraph is not compatible with old
|
|
|
|
Handles leaping from paragraph to paragraph (due to vanished text) on
|
|
behalf of FetchAppendEscCore(). If the new paragraph is compatible
|
|
with the old one, FetchPap is called and text is informed of the
|
|
new para end parameters.
|
|
----------------------------------------------------------------------------*/
|
|
static LSERR CheckNewPara(PLSC plsc, LSCP cpPrev, LSCP cpThis, BOOL* pfQuit)
|
|
{
|
|
LSERR lserr;
|
|
BOOL fHazard;
|
|
LSPAP lspap;
|
|
DWORD iobjText;
|
|
PLNOBJ plnobjText;
|
|
|
|
*pfQuit = fTrue;
|
|
|
|
Assert(cpThis >= 0 && cpThis > cpPrev);
|
|
|
|
|
|
|
|
|
|
lserr = plsc->lscbk.pfnCheckParaBoundaries(plsc->pols, cpPrev, cpThis, &fHazard);
|
|
if (lserr != lserrNone)
|
|
return lserr;
|
|
|
|
if (!fHazard)
|
|
{
|
|
|
|
lserr = plsc->lscbk.pfnFetchPap(plsc->pols, cpThis, &lspap);
|
|
if (lserr != lserrNone)
|
|
return lserr;
|
|
|
|
/* we don't know are we really in a new paragraph or not */
|
|
/* so we have to modify information about end of paragraph */
|
|
/* always as would we are in a new paragraph */
|
|
iobjText = IobjTextFromLsc(&plsc->lsiobjcontext);
|
|
plnobjText = PlnobjFromLsc(plsc, iobjText);
|
|
|
|
lserr = ModifyTextLineEnding(plnobjText, lspap.lskeop);
|
|
if (lserr != lserrNone)
|
|
return lserr;
|
|
|
|
SetCpInPara(plsc->lstabscontext, cpThis);
|
|
plsc->fLimSplat = lspap.grpf & fFmiLimSplat;
|
|
plsc->fIgnoreSplatBreak = lspap.grpf & fFmiIgnoreSplatBreak;
|
|
|
|
/* we don't invalidate tabs info and other paragraph properties
|
|
/* that we stored in context */
|
|
|
|
*pfQuit = fFalse;
|
|
}
|
|
|
|
return lserr;
|
|
}
|
|
|
|
|
|
/* F L I M I T R U N E S C */
|
|
/*----------------------------------------------------------------------------
|
|
%%Function: FLimitRunEsc
|
|
%%Contact: igorzv
|
|
Parameters:
|
|
plsfrun - (IN) run to cut
|
|
plsesc - (IN) set of esc characters
|
|
iescLim - (IN) number of esc characters
|
|
|
|
On behalf of LsFetchAppendEscCore(), this routine limits a run when
|
|
an escape character is present.
|
|
----------------------------------------------------------------------------*/
|
|
static BOOL FLimitRunEsc(LSFRUN* plsfrun, const LSESC* plsesc, DWORD iescLim)
|
|
{
|
|
DWORD iesc;
|
|
DWORD ich;
|
|
const LPCWSTR pwch = plsfrun->lpwchRun;
|
|
const DWORD ichLim = plsfrun->cwchRun;
|
|
|
|
Assert(iescLim > 0); /* optimization -- test before calling */
|
|
|
|
for (ich=0; ich<ichLim; ich++)
|
|
{
|
|
for (iesc=0; iesc<iescLim; iesc++)
|
|
{
|
|
|
|
if (FBetween(pwch[ich], plsesc[iesc].wchFirst, plsesc[iesc].wchLast))
|
|
{
|
|
plsfrun->cwchRun = ich;
|
|
return fTrue;
|
|
}
|
|
}
|
|
}
|
|
return fFalse;
|
|
}
|
|
|
|
|
|
|
|
/* F O R M A T A N M */
|
|
/*----------------------------------------------------------------------------
|
|
%%Function: FormatAnm
|
|
%%Contact: igorzv
|
|
Parameters:
|
|
plsc - (IN) ptr to line services context
|
|
plsfrunMainText - (IN) first run of the main text
|
|
|
|
Formats and allignes bullets and numbering
|
|
----------------------------------------------------------------------------*/
|
|
|
|
LSERR FormatAnm(PLSC plsc, PLSFRUN plsfrunMainText)
|
|
{
|
|
long duaSpaceAnm;
|
|
long duaWidthAnm;
|
|
LSKALIGN lskalignAnm;
|
|
WCHAR wchAdd;
|
|
BOOL fWord95Model;
|
|
LSERR lserr;
|
|
LSFRUN lsfrun;
|
|
LSCHP lschp; /* local memory to store lschp */
|
|
FMTRES fmtres;
|
|
long durUsed;
|
|
long urOriginal;
|
|
long durAfter = 0;
|
|
long durBefore = 0;
|
|
LSCP cpLimOriginal;
|
|
OBJDIM* pobjdimAnm;
|
|
PLSDNODE plsdnAllignmentTab;
|
|
BOOL fInterruptBorder;
|
|
LSCHP lschpAdd; /* lschp for character added after autonumber */
|
|
PLSRUN plsrunAdd; /* plsrun for character added after autonumber */
|
|
|
|
|
|
Assert(FIsLSC(plsc));
|
|
Assert(FFormattingAllowed(plsc));
|
|
|
|
|
|
/*Initialization; */
|
|
lsfrun.plschp = &lschp;
|
|
cpLimOriginal = GetCurrentCpLim(plsc);
|
|
urOriginal = GetCurrentUr(plsc);
|
|
SetCurrentCpLim(plsc, cpFirstAnm);
|
|
|
|
/* get autonumbering information */
|
|
lserr = plsc->lscbk.pfnGetAutoNumberInfo(plsc->pols, &lskalignAnm, &lschp, &lsfrun.plsrun,
|
|
&wchAdd, &lschpAdd, &plsrunAdd,
|
|
&fWord95Model, &duaSpaceAnm, &duaWidthAnm);
|
|
if (lserr != lserrNone)
|
|
return lserr;
|
|
|
|
Assert(!memcmp(&lschp, &lschpAdd, sizeof(lschpAdd)));
|
|
|
|
lsfrun.cwchRun = 0 ; /* we dont use characters in formating autonumbering object */
|
|
lsfrun.lpwchRun = NULL;
|
|
|
|
/* put idobj of autonumber to lschp */
|
|
lschp.idObj = (WORD) IobjAutonumFromLsc(&plsc->lsiobjcontext);
|
|
|
|
/* remove underlining and some other bits from chp */
|
|
/* we don't underline it as a whole */
|
|
lschp.fUnderline = fFalse;
|
|
lschp.fStrike = fFalse;
|
|
lschp.fShade = fFalse;
|
|
lschp.EffectsFlags = 0;
|
|
|
|
lserr = ProcessOneRun(plsc, uLsInfiniteRM, &lsfrun, NULL,
|
|
0, &fmtres);
|
|
if (lserr != lserrNone)
|
|
return lserr;
|
|
|
|
Assert(fmtres == fmtrCompletedRun);
|
|
Assert(GetCurrentDnode(plsc) != NULL);
|
|
|
|
|
|
/* store heights of autonumber */
|
|
Assert(FIsDnodeReal(GetCurrentDnode(plsc)));
|
|
pobjdimAnm = &(GetCurrentDnode(plsc)->u.real.objdim);
|
|
plsc->plslineCur->lslinfo.dvpAscentAutoNumber = pobjdimAnm->heightsPres.dvAscent;
|
|
plsc->plslineCur->lslinfo.dvrAscentAutoNumber = pobjdimAnm->heightsRef.dvAscent;
|
|
plsc->plslineCur->lslinfo.dvpDescentAutoNumber = pobjdimAnm->heightsPres.dvDescent;
|
|
plsc->plslineCur->lslinfo.dvrDescentAutoNumber = pobjdimAnm->heightsRef.dvDescent;
|
|
|
|
|
|
if (wchAdd != 0) /* fill in lsfrun with a run of one character */
|
|
{
|
|
lsfrun.plschp = &lschpAdd;
|
|
lsfrun.plsrun = plsrunAdd;
|
|
lsfrun.lpwchRun = &wchAdd;
|
|
lsfrun.cwchRun = 1;
|
|
|
|
lserr = ProcessOneRun(plsc, uLsInfiniteRM, &lsfrun, NULL,
|
|
0, &fmtres);
|
|
if (lserr != lserrNone)
|
|
return lserr;
|
|
|
|
Assert(fmtres == fmtrCompletedRun || fmtres == fmtrTab);
|
|
}
|
|
|
|
plsdnAllignmentTab = GetCurrentDnode(plsc); /* in the case when added character is not tab this
|
|
value will not be used */
|
|
|
|
if (lsfrun.plschp->fBorder)
|
|
{
|
|
if (plsfrunMainText->plschp->fBorder)
|
|
{
|
|
/* check that client wants to border runs together */
|
|
lserr = plsc->lscbk.pfnFInterruptBorder(plsc->pols,
|
|
lsfrun.plsrun, plsfrunMainText->plsrun, &fInterruptBorder);
|
|
if (lserr != lserrNone)
|
|
return lserr;
|
|
|
|
if (fInterruptBorder)
|
|
{
|
|
/* we should close border before allignment */
|
|
lserr = CloseCurrentBorder(plsc);
|
|
if (lserr != lserrNone)
|
|
return lserr;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* we should close border before allignment */
|
|
lserr = CloseCurrentBorder(plsc);
|
|
if (lserr != lserrNone)
|
|
return lserr;
|
|
}
|
|
}
|
|
|
|
durUsed = GetCurrentUr(plsc) - urOriginal;
|
|
|
|
if (fWord95Model)
|
|
{
|
|
Assert(wchAdd != 0);
|
|
Assert(fmtres == fmtrTab);
|
|
|
|
AllignAutonum95(UrFromUa(LstflowFromSubline(GetCurrentSubline(plsc)),
|
|
&(plsc->lsdocinf.lsdevres), duaSpaceAnm),
|
|
UrFromUa(LstflowFromSubline(GetCurrentSubline(plsc)),
|
|
&(plsc->lsdocinf.lsdevres), duaWidthAnm),
|
|
lskalignAnm, durUsed, plsdnAllignmentTab,
|
|
&durBefore, &durAfter);
|
|
}
|
|
else
|
|
{
|
|
lserr = AllignAutonum(&(plsc->lstabscontext), lskalignAnm,
|
|
(wchAdd != 0 && fmtres == fmtrTab),
|
|
plsdnAllignmentTab, GetCurrentUr(plsc),
|
|
durUsed, &durBefore, &durAfter);
|
|
if (lserr != lserrNone)
|
|
return lserr;
|
|
/* if there is no allignment after then durAfter should be zero */
|
|
Assert(!((durAfter != 0) && (!(wchAdd != 0 && fmtres == fmtrTab))));
|
|
}
|
|
|
|
/* change geometry because of durBefore */
|
|
plsc->lsadjustcontext.urStartAutonumberingText =
|
|
plsc->lsadjustcontext.urLeftIndent + durBefore;
|
|
AdvanceCurrentUr(plsc, durBefore);
|
|
|
|
/* change geometry because of durAfter */
|
|
AdvanceCurrentUr(plsc, durAfter);
|
|
|
|
plsc->lsadjustcontext.urStartMainText = GetCurrentUr(plsc);
|
|
|
|
/* restore cpLim */
|
|
SetCurrentCpLim(plsc, cpLimOriginal);
|
|
|
|
return lserrNone;
|
|
}
|
|
|
|
#define iobjAutoDecimalTab (idObjTextChp-1)
|
|
|
|
/* I N I T I A L I Z E A U T O D E C T A B */
|
|
/*----------------------------------------------------------------------------
|
|
%%Function: InitializeAutoDecTab
|
|
%%Contact: igorzv
|
|
Parameters:
|
|
plsc - (IN) ptr to line services context
|
|
durAutoDecimalTab - (IN) auto decimal tab offset
|
|
|
|
Creates tab stop record and dnode for "auto-decimal tab"
|
|
----------------------------------------------------------------------------*/
|
|
|
|
|
|
LSERR InitializeAutoDecTab(PLSC plsc, long durAutoDecimalTab)
|
|
|
|
{
|
|
PLSDNODE plsdnTab;
|
|
LSERR lserr;
|
|
LSKTAB lsktab;
|
|
BOOL fBreakThroughTab;
|
|
LSCP cpLimOriginal;
|
|
|
|
if (durAutoDecimalTab > GetCurrentUr(plsc))
|
|
{
|
|
cpLimOriginal = GetCurrentCpLim(plsc);
|
|
SetCurrentCpLim(plsc, LONG_MIN + 1);
|
|
|
|
lserr = InitTabsContextForAutoDecimalTab(&plsc->lstabscontext, durAutoDecimalTab);
|
|
if (lserr != lserrNone)
|
|
return lserrNone;
|
|
|
|
CreateDnode(plsc, plsdnTab);
|
|
|
|
*(GetWhereToPutLink(plsc, plsdnTab->plsdnPrev)) = plsdnTab;
|
|
SetCurrentDnode(plsc, plsdnTab);
|
|
|
|
/* fill in this dnode */
|
|
memset(&plsdnTab->u.real.objdim, 0, sizeof(OBJDIM));
|
|
memset(&plsdnTab->u.real.lschp, 0, sizeof(LSCHP));
|
|
plsdnTab->u.real.lschp.idObj = (WORD) IobjTextFromLsc(&plsc->lsiobjcontext);
|
|
plsdnTab->fTab = fTrue;
|
|
plsdnTab->fAutoDecTab = fTrue;
|
|
plsdnTab->cpLimOriginal = cpLimOriginal; /* it's important to display to put correct value here */
|
|
plsdnTab->dcp = 0;
|
|
|
|
/* If PrepareLineToDisplay is not called, this dnode will not convert to pen and will destroyed
|
|
as real dnode. So we need to put NULL to plsrun, pdobj, pinfosubl*/
|
|
plsdnTab->u.real.plsrun = NULL;
|
|
plsdnTab->u.real.pdobj = NULL;
|
|
plsdnTab->u.real.pinfosubl = NULL;
|
|
|
|
lserr = GetCurTabInfoCore(&plsc->lstabscontext, plsdnTab, GetCurrentUr(plsc),
|
|
fFalse, &lsktab, &fBreakThroughTab);
|
|
if (lserr != lserrNone)
|
|
return lserr;
|
|
|
|
TurnOnTabEncounted(plsc);
|
|
if (lsktab != lsktLeft)
|
|
TurnOnNonLeftTabEncounted(plsc);
|
|
|
|
/* restore cpLim */
|
|
SetCurrentCpLim(plsc, cpLimOriginal);
|
|
|
|
TurnOnAutodecimalTabPresent(plsc);
|
|
}
|
|
return lserrNone;
|
|
}
|
|
|
|
/* H A N D L E T A B */
|
|
/*----------------------------------------------------------------------------
|
|
%%Function: HandleTab
|
|
%%Contact: igorzv
|
|
Parameters:
|
|
plsc - (IN) ptr to line services context
|
|
|
|
Wraper around calls to tabutils module.
|
|
----------------------------------------------------------------------------*/
|
|
|
|
LSERR HandleTab(PLSC plsc)
|
|
{
|
|
LSKTAB lsktab;
|
|
LSERR lserr;
|
|
BOOL fBreakThroughTab;
|
|
long durPendingTab;
|
|
long urNewMargin;
|
|
|
|
/* if we are not on a stage of a formatting this procedure resolve previous tab if any */
|
|
|
|
/* before tab calculation we should resolve pending tab */
|
|
lserr = ResolvePrevTabCore(&plsc->lstabscontext, GetCurrentDnode(plsc),
|
|
GetCurrentUr(plsc), &durPendingTab);
|
|
if (lserr != lserrNone)
|
|
return lserr;
|
|
/* move current pen position */
|
|
Assert(durPendingTab >= 0);
|
|
AdvanceCurrentUr(plsc, durPendingTab);
|
|
|
|
if (FFormattingAllowed(plsc))
|
|
{
|
|
/* in this case we are called only after tab */
|
|
Assert(GetCurrentDnode(plsc)->fTab);
|
|
lserr = GetCurTabInfoCore(&plsc->lstabscontext, GetCurrentDnode(plsc), GetCurrentUr(plsc),
|
|
fFalse, &lsktab, &fBreakThroughTab);
|
|
if (lserr != lserrNone)
|
|
return lserr;
|
|
|
|
TurnOnTabEncounted(plsc);
|
|
if (lsktab != lsktLeft)
|
|
TurnOnNonLeftTabEncounted(plsc);
|
|
|
|
/* move current pen position */
|
|
AdvanceCurrentUr(plsc, DurFromDnode(GetCurrentDnode(plsc)));
|
|
|
|
if (fBreakThroughTab)
|
|
{
|
|
lserr = GetMarginAfterBreakThroughTab(&plsc->lstabscontext, GetCurrentDnode(plsc),
|
|
&urNewMargin);
|
|
if (lserr != lserrNone)
|
|
return lserr;
|
|
|
|
SetBreakthroughLine(plsc, urNewMargin);
|
|
}
|
|
}
|
|
return lserrNone;
|
|
}
|
|
|
|
#define idObjSplat idObjTextChp - 2
|
|
|
|
/* H A N D L E S P L A T */
|
|
/*----------------------------------------------------------------------------
|
|
%%Function: HandleSplat
|
|
%%Contact: igorzv
|
|
Parameters:
|
|
plsc - (IN) ptr to line services context
|
|
pfmtres - (OUT) fmtres of the splat dnode, procedure can change it
|
|
in the case of fIgnoreSplatBreak to either fmtrCompletedRun
|
|
or fmtrStopped
|
|
|
|
Markes dnode for splat, deletes it in a case of fIgnoreSplatBreak
|
|
----------------------------------------------------------------------------*/
|
|
|
|
LSERR HandleSplat(PLSC plsc, FMTRES* pfmtres)
|
|
{
|
|
PLSDNODE plsdn;
|
|
LSCP cpAfterSplat;
|
|
BOOL fQuit;
|
|
LSERR lserr;
|
|
|
|
plsdn = GetCurrentDnode(plsc);
|
|
cpAfterSplat = GetCurrentCpLim(plsc);
|
|
|
|
if (plsc->fIgnoreSplatBreak)
|
|
{
|
|
lserr = CheckNewPara(plsc, cpAfterSplat - 1, cpAfterSplat, &fQuit);
|
|
if (lserr != lserrNone)
|
|
return lserr;
|
|
|
|
if (fQuit)
|
|
{
|
|
/* despite plsc->fIgnoreSplatBreak we should stop formatting here */
|
|
*pfmtres = fmtrStopped;
|
|
}
|
|
else
|
|
{
|
|
*pfmtres = fmtrCompletedRun;
|
|
}
|
|
|
|
/* delete splat dnode */
|
|
/* break link */
|
|
*(GetWhereToPutLink(plsc, plsdn->plsdnPrev)) = NULL;
|
|
|
|
/* restore current dnode, don't change cpLim and geometry */
|
|
SetCurrentDnode(plsc, plsdn->plsdnPrev);
|
|
|
|
Assert(plsdn->plsdnNext == NULL);
|
|
lserr = DestroyDnodeList (&plsc->lscbk, plsc->pols, &plsc->lsiobjcontext,
|
|
plsdn, plsc->fDontReleaseRuns);
|
|
if (lserr != lserrNone)
|
|
return lserr;
|
|
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
/* set special idobj that will solve all chunk group chunk problems */
|
|
Assert(FIsDnodeReal(plsdn));
|
|
plsdn->u.real.lschp.idObj = idObjSplat;
|
|
TurnOffAllSimpleText(plsc); /* not simple text */
|
|
|
|
}
|
|
|
|
return lserrNone;
|
|
}
|
|
|
|
/* C R E A T E S U B L I N E C O R E */
|
|
/*----------------------------------------------------------------------------
|
|
%%Function: CreateSublineCore
|
|
%%Contact: igorzv
|
|
Parameters:
|
|
plsc - (IN) ptr to line services context
|
|
cpFirst - (IN) first cp of a subline
|
|
urColumnMax - (IN) max possible width of a subline
|
|
lstflow - (IN) text flow of a subline
|
|
fContiguos - (IN) if TRUE such line has the same coordinate system as main line
|
|
and is allowed to have tabs
|
|
|
|
Allocates, initializes subline structure. Sets subline as current.
|
|
----------------------------------------------------------------------------*/
|
|
|
|
LSERR CreateSublineCore(PLSC plsc, LSCP cpFirst, long urColumnMax,
|
|
LSTFLOW lstflow, BOOL fContiguous)
|
|
{
|
|
PLSSUBL plssubl;
|
|
LSERR lserr;
|
|
|
|
Assert(FIsLSC(plsc));
|
|
Assert(FFormattingAllowed(plsc) || FBreakingAllowed(plsc));
|
|
Assert(GetCurrentSubline(plsc) == NULL);
|
|
|
|
plssubl = plsc->lscbk.pfnNewPtr(plsc->pols,
|
|
sizeof(LSSUBL));
|
|
if (plssubl == NULL)
|
|
return lserrOutOfMemory;
|
|
|
|
/* fill in structure */
|
|
plssubl->tag = tagLSSUBL;
|
|
plssubl->plsc = plsc;
|
|
plssubl->cpFirst = cpFirst;
|
|
plssubl->lstflow = lstflow;
|
|
plssubl->urColumnMax = urColumnMax;
|
|
plssubl->cpLim = cpFirst;
|
|
plssubl->plsdnFirst = NULL;
|
|
plssubl->plsdnLast = NULL;
|
|
plssubl->fMain = fFalse;
|
|
plssubl->plsdnUpTemp = NULL;
|
|
plssubl->fAcceptedForDisplay = fFalse;
|
|
plssubl->fRightMarginExceeded = fFalse;
|
|
|
|
if (fContiguous)
|
|
{
|
|
Assert(FFormattingAllowed(plsc));
|
|
Assert(SublineFromDnode(GetDnodeToFinish(plsc))->fContiguous);
|
|
plssubl->urCur = GetCurrentUrSubl(SublineFromDnode(GetDnodeToFinish(plsc)));
|
|
plssubl->vrCur = GetCurrentVrSubl(SublineFromDnode(GetDnodeToFinish(plsc)));
|
|
}
|
|
else
|
|
{
|
|
plssubl->urCur = 0;
|
|
plssubl->vrCur = 0;
|
|
}
|
|
plssubl->fContiguous = (BYTE) fContiguous;
|
|
plssubl->fDupInvalid = fTrue;
|
|
|
|
plssubl->plschunkcontext = plsc->lscbk.pfnNewPtr(plsc->pols,
|
|
sizeof(LSCHUNKCONTEXT));
|
|
if (plssubl->plschunkcontext == NULL)
|
|
return lserrOutOfMemory;
|
|
|
|
lserr = AllocChunkArrays(plssubl->plschunkcontext, &plsc->lscbk, plsc->pols,
|
|
&plsc->lsiobjcontext);
|
|
if (lserr != lserrNone)
|
|
return lserr;
|
|
|
|
InitSublineChunkContext(plssubl->plschunkcontext, plssubl->urCur, plssubl->vrCur);
|
|
|
|
/* allocate break context */
|
|
plssubl->pbrkcontext = plsc->lscbk.pfnNewPtr(plsc->pols,
|
|
sizeof(BRKCONTEXT));
|
|
if (plssubl->pbrkcontext == NULL)
|
|
return lserrOutOfMemory;
|
|
/* set flags */
|
|
plssubl->pbrkcontext->fBreakPrevValid = fFalse;
|
|
plssubl->pbrkcontext->fBreakNextValid = fFalse;
|
|
plssubl->pbrkcontext->fBreakForceValid = fFalse;
|
|
|
|
|
|
/* set this subline as current */
|
|
SetCurrentSubline(plsc, plssubl);
|
|
|
|
IncreaseFormatDepth(plsc);
|
|
|
|
return lserrNone;
|
|
}
|
|
|
|
|
|
/* F I N I S H S U B L I N E C O R E */
|
|
/*----------------------------------------------------------------------------
|
|
%%Function: FinishSublineCore
|
|
%%Contact: igorzv
|
|
Parameters:
|
|
plssubl - (IN) subline to finish
|
|
|
|
Applies nominal to ideal to the last chunk of text, flushes current subline
|
|
----------------------------------------------------------------------------*/
|
|
|
|
|
|
LSERR FinishSublineCore(
|
|
PLSSUBL plssubl) /* IN: subline to finish */
|
|
{
|
|
PLSC plsc;
|
|
LSERR lserr;
|
|
PLSDNODE plsdn;
|
|
|
|
Assert(FIsLSSUBL(plssubl));
|
|
|
|
plsc = plssubl->plsc;
|
|
Assert(plssubl == GetCurrentSubline(plsc));
|
|
|
|
/* apply nominal to ideal to the last chunk of text */
|
|
if (FNominalToIdealEncounted(plsc))
|
|
{
|
|
lserr = ApplyNominalToIdeal(PlschunkcontextFromSubline(plssubl), &plsc->lsiobjcontext,
|
|
plsc->grpfManager, plsc->lsadjustcontext.lskj,
|
|
FIsSubLineMain(plssubl), FLineContainsAutoNumber(plsc),
|
|
GetCurrentDnodeSubl(plssubl));
|
|
if (lserr != lserrNone)
|
|
return lserr;
|
|
}
|
|
|
|
/* skip back pen dnodes */
|
|
plsdn = plssubl->plsdnLast;
|
|
while (plsdn != NULL && FIsDnodePen(plsdn))
|
|
{
|
|
plsdn = plsdn->plsdnPrev;
|
|
}
|
|
|
|
/* close last border */
|
|
if (FDnodeHasBorder(plsdn) && !FIsDnodeCloseBorder(plsdn))
|
|
{
|
|
lserr = CloseCurrentBorder(plsc);
|
|
if (lserr != lserrNone)
|
|
return lserr;
|
|
}
|
|
|
|
/* set boundaries for display */
|
|
SetCpLimDisplaySubl(plssubl, GetCurrentCpLimSubl(plssubl));
|
|
SetLastDnodeDisplaySubl(plssubl, GetCurrentDnodeSubl(plssubl));
|
|
|
|
|
|
/* flush current subline */
|
|
SetCurrentSubline(plsc, NULL);
|
|
|
|
DecreaseFormatDepth(plsc);
|
|
|
|
lserr = LsSublineFinishedText(PlnobjFromLsc(plsc, IobjTextFromLsc(&((plsc)->lsiobjcontext))));
|
|
if (lserr != lserrNone)
|
|
return lserr;
|
|
|
|
return lserrNone;
|
|
}
|
|
|
|
/* U N D O L A S T D N O D E */
|
|
/*----------------------------------------------------------------------------
|
|
%%Function: UndoLastDnode
|
|
%%Contact: igorzv
|
|
Parameters:
|
|
plsc - (IN) ptr to line services context
|
|
|
|
Restores set before last dnode and deletes it.
|
|
----------------------------------------------------------------------------*/
|
|
|
|
static LSERR UndoLastDnode(PLSC plsc)
|
|
{
|
|
PLSDNODE plsdn = GetCurrentDnode(plsc);
|
|
long cpDecrease;
|
|
|
|
Assert(FIsLSDNODE(plsdn));
|
|
|
|
/* break link */
|
|
*(GetWhereToPutLink(plsc, plsdn->plsdnPrev)) = NULL;
|
|
|
|
/* restore state */
|
|
cpDecrease = plsdn->dcp;
|
|
AdvanceCurrentCpLim(plsc, -cpDecrease);
|
|
SetCurrentDnode(plsc, plsdn->plsdnPrev);
|
|
AdvanceCurrentUr(plsc, -DurFromDnode(plsdn));
|
|
AdvanceCurrentVr(plsc, -DvrFromDnode(plsdn));
|
|
|
|
Assert(plsdn->plsdnNext == NULL);
|
|
return DestroyDnodeList (&plsc->lscbk, plsc->pols, &plsc->lsiobjcontext,
|
|
plsdn, plsc->fDontReleaseRuns);
|
|
|
|
|
|
}
|
|
|
|
/* O P E N B O R D E R */
|
|
/*----------------------------------------------------------------------------
|
|
%%Function: OpenBorder
|
|
%%Contact: igorzv
|
|
Parameters:
|
|
plsc - (IN) ptr to line services context
|
|
plsrun - (IN) run with border information
|
|
|
|
Creates border dnode
|
|
----------------------------------------------------------------------------*/
|
|
static LSERR OpenBorder(PLSC plsc, PLSRUN plsrun)
|
|
{
|
|
PLSDNODE plsdnCurrent;
|
|
PLSDNODE* pplsdnToStoreNext;
|
|
long durBorder, dupBorder;
|
|
PLSDNODE plsdnBorder;
|
|
LSERR lserr;
|
|
|
|
plsdnCurrent = GetCurrentDnode(plsc);
|
|
pplsdnToStoreNext = GetWhereToPutLink(plsc, plsdnCurrent);
|
|
|
|
lserr = plsc->lscbk.pfnGetBorderInfo(plsc->pols, plsrun, GetCurrentLstflow(plsc),
|
|
&durBorder, &dupBorder);
|
|
if (lserr != lserrNone)
|
|
return lserr;
|
|
|
|
|
|
CreateBorderDnode(plsc, plsdnBorder, durBorder, dupBorder);
|
|
plsdnBorder->fOpenBorder = fTrue;
|
|
|
|
/* maintain list and state */
|
|
*pplsdnToStoreNext = plsdnBorder;
|
|
SetCurrentDnode(plsc, plsdnBorder);
|
|
AdvanceCurrentUr(plsc, durBorder);
|
|
TurnOffAllSimpleText(plsc); /* not simple text */
|
|
|
|
return lserrNone;
|
|
}
|
|
|
|
/* C L O S E C U R R E N T B O R D E R */
|
|
/*----------------------------------------------------------------------------
|
|
%%Function: CloseCurrentBorder
|
|
%%Contact: igorzv
|
|
Parameters:
|
|
plsc - (IN) ptr to line services context
|
|
|
|
Creates border dnode
|
|
----------------------------------------------------------------------------*/
|
|
LSERR CloseCurrentBorder(PLSC plsc)
|
|
{
|
|
PLSDNODE plsdnCurrent;
|
|
PLSDNODE* pplsdnToStoreNext;
|
|
long durBorder, dupBorder;
|
|
PLSDNODE plsdnBorder;
|
|
LSERR lserr;
|
|
PLSDNODE plsdn;
|
|
|
|
plsdnCurrent = GetCurrentDnode(plsc);
|
|
pplsdnToStoreNext = GetWhereToPutLink(plsc, plsdnCurrent);
|
|
|
|
/* find open border */
|
|
plsdn = plsdnCurrent;
|
|
Assert(FIsLSDNODE(plsdn));
|
|
while (! FIsDnodeBorder(plsdn))
|
|
{
|
|
plsdn = plsdn->plsdnPrev;
|
|
Assert(FIsLSDNODE(plsdn));
|
|
}
|
|
Assert(plsdn->fOpenBorder);
|
|
|
|
if (plsdn != plsdnCurrent)
|
|
{
|
|
durBorder = plsdn->u.pen.dur;
|
|
dupBorder = plsdn->u.pen.dup;
|
|
|
|
CreateBorderDnode(plsc, plsdnBorder, durBorder, dupBorder);
|
|
|
|
/* maintain list and state */
|
|
*pplsdnToStoreNext = plsdnBorder;
|
|
SetCurrentDnode(plsc, plsdnBorder);
|
|
AdvanceCurrentUr(plsc, durBorder);
|
|
}
|
|
else
|
|
{
|
|
/* we have empty list between borders */
|
|
lserr = UndoLastDnode(plsc);
|
|
if (lserr != lserrNone)
|
|
return lserrNone;
|
|
}
|
|
|
|
return lserrNone;
|
|
}
|
|
|
|
long RightMarginIncreasing(PLSC plsc, long urColumnMax)
|
|
{
|
|
long Coeff = plsc->lMarginIncreaseCoefficient;
|
|
long urInch;
|
|
long One32rd;
|
|
if (urColumnMax <= 0)
|
|
{
|
|
/* such strange formula for non positive margin is to have on
|
|
the first iteration 1 inch and 8 inches on the second*/
|
|
urInch = UrFromUa(LstflowFromSubline(GetCurrentSubline(plsc)),
|
|
&(plsc)->lsdocinf.lsdevres, 1440);
|
|
if (Coeff == uLsInfiniteRM || (Coeff >= uLsInfiniteRM / (7 * urInch)))
|
|
return uLsInfiniteRM;
|
|
else
|
|
return (7*Coeff - 6)* urInch;
|
|
}
|
|
else
|
|
{
|
|
if (urColumnMax <= 32)
|
|
One32rd = 1;
|
|
else
|
|
One32rd = urColumnMax >> 5;
|
|
|
|
if (Coeff == uLsInfiniteRM || (Coeff >= (uLsInfiniteRM - urColumnMax)/One32rd))
|
|
return uLsInfiniteRM;
|
|
else
|
|
return urColumnMax + (Coeff * One32rd);
|
|
}
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------
|
|
/* E R R R E L E A S E R U N T O F O R M A T */
|
|
/*----------------------------------------------------------------------------
|
|
%%Function: ErrReleaseRunToFormat
|
|
%%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 run has not been formatted yet .
|
|
----------------------------------------------------------------------------*/
|
|
static LSERR ErrReleaseRunToFormat(PLSC plsc, PLSRUN plsrun, LSERR lserr)
|
|
{
|
|
LSERR lserrIgnore;
|
|
|
|
if (!plsc->fDontReleaseRuns)
|
|
lserrIgnore = plsc->lscbk.pfnReleaseRun(plsc->pols, plsrun);
|
|
|
|
return lserr;
|
|
}
|
|
|
|
|