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

635 lines
18 KiB
C

#include "lsqcore.h"
#include "lsc.h"
#include "lsqsinfo.h"
#include "lsdnode.h"
#include "lssubl.h"
#include "heights.h"
#include "lschp.h"
#include "iobj.h"
#include "lsqin.h"
#include "lsqout.h"
#include "dninfo.h"
#include "lssubset.h"
#include "lstfset.h"
#include "dispmisc.h"
#define FIsInContent(pdn) (!FIsNotInContent(pdn))
#define FIsZeroWidth(pdn) (FIsDnodeReal(pdn) && (pdn)->u.real.dup == 0)
#define FIsDnodeClosingBorder(pdn) (FIsDnodeBorder(pdn) && (!(pdn)->fOpenBorder))
static void PrepareQueryCall(PLSSUBL, PLSDNODE, LSQIN*);
static LSERR FillInQueryResults(PLSC, PLSSUBL, PLSQSUBINFO, PLSDNODE, POINTUV*, LSQOUT*);
static void FillInTextCellInfo(PLSC, PLSDNODE, POINTUV*, LSQOUT*, PLSTEXTCELL);
static void TransformPointsOnLowerLevels(PLSQSUBINFO, DWORD, PLSTEXTCELL, PPOINTUV, LSTFLOW, LSTFLOW);
static void ApplyFormula(PPOINTUV, DWORD[], PPOINTUV);
static PLSDNODE BacktrackToPreviousDnode(PLSDNODE pdn, POINTUV* pt);
static PLSDNODE AdvanceToNextDnodeQuery(PLSDNODE, PPOINTUV);
// %%Function: QuerySublineCpPpointCore
// %%Contact: victork
//
/*
* Returns dim-info of the cp in the subline.
*
* If that cp isn't displayed in the line, take closest to the left that is displayed.
* If that's impossible, go to the right.
*
* Hidden text inside ligature makes it impossible to tell whether a particular cp is hidden or not
*/
LSERR QuerySublineCpPpointCore(
PLSSUBL plssubl,
LSCP cp, /* IN: cpQuery */
DWORD cDepthQueryMax, /* IN: allocated size of results array */
PLSQSUBINFO plsqsubinfoResults, /* OUT: array[cDepthQueryMax] of results */
DWORD* pcActualDepth, /* OUT: size of results array (filled) */
PLSTEXTCELL plstextcell) /* OUT: Text cell info */
{
PLSC plsc;
LSERR lserr = lserrNone;
PLSDNODE pdn, pdnPrev = NULL;
POINTUV pt;
LSCP cpLim;
LSQIN lsqin;
LSQOUT lsqout;
PLSSUBL plssublLowerLevels;
POINTUV ptStartLowerLevels;
PLSQSUBINFO plsqsubinfoLowerLevels;
DWORD cDepthQueryMaxLowerLevels;
DWORD cActualDepthLowerLevels;
Assert(FIsLSSUBL(plssubl));
Assert(!plssubl->fDupInvalid);
if (cDepthQueryMax == 0)
{
return lserrInsufficientQueryDepth;
}
plsc = plssubl->plsc;
cpLim = plssubl->cpLimDisplay;
pt.u = 0;
pt.v = 0;
pdn = plssubl->plsdnFirst;
/* Skip over autonumbers & starting pens/borders */
while (FDnodeBeforeCpLim(pdn, cpLim) && (FIsNotInContent(pdn) || !(FIsDnodeReal(pdn))))
{
pdn = AdvanceToNextDnodeQuery(pdn, &pt);
}
if (!FDnodeBeforeCpLim(pdn, cpLim))
{ /* empty subline */
*pcActualDepth = 0;
return lserrNone;
}
// if cp <= pdn->cpFirst, pdn is the dnode to query, else...
if (cp > pdn->cpFirst)
{
/* Skip dnodes before the cp */
while (FDnodeBeforeCpLim(pdn, cpLim) && pdn->cpFirst + pdn->dcp <= (LSDCP)cp)
{
pdnPrev = pdn;
pdn = AdvanceToNextDnodeQuery(pdn, &pt);
}
/* go back if our cp is in vanished text or pen or border */
if (!FDnodeBeforeCpLim(pdn, cpLim) || // reached the end
pdn->cpFirst > cp || // went too far because of hidden text
!(FIsDnodeReal(pdn))) // our cp points to a pen
{
Assert(pdnPrev != NULL); // we made at least one forward step
pdn = pdnPrev;
pdnPrev = BacktrackToPreviousDnode(pdnPrev, &pt);
// skip all pens/borders
while (pdn != NULL && FIsInContent(pdn) && !(FIsDnodeReal(pdn)))
{
pdn = pdnPrev;
pdnPrev = BacktrackToPreviousDnode(pdnPrev, &pt);
}
// nothing good to the left situation is impossible
Assert(pdn != NULL && !FIsNotInContent(pdn));
}
}
/* we've found the dnode, have pt just before it, ask method for details */
if (cp >= (LSCP) (pdn->cpFirst + pdn->dcp)) /* cp in next vanished piece */
cp = pdn->cpFirst + pdn->dcp - 1; /* query last cp */
if (cp < (LSCP) pdn->cpFirst) /* cp in a previous pen */
cp = pdn->cpFirst; /* query first cp */
pt.v += pdn->u.real.lschp.dvpPos; // go to the local baseline
PrepareQueryCall(plssubl, pdn, &lsqin);
lserr = (*plsc->lsiobjcontext.rgobj[pdn->u.real.lschp.idObj].lsim.pfnQueryCpPpoint)
(pdn->u.real.pdobj, cp - pdn->cpFirst, &lsqin, &lsqout);
if (lserr != lserrNone)
return lserr;
lserr = FillInQueryResults(plsc, plssubl, plsqsubinfoResults, pdn, &pt, &lsqout);
if (lserr != lserrNone)
return lserr;
if (lsqout.plssubl == NULL) // terminal object
{
*pcActualDepth = 1;
FillInTextCellInfo(plsc, pdn, &pt, &lsqout, plstextcell);
}
else // there are more level(s)
{
// recursive call to fill lower levels
plssublLowerLevels = lsqout.plssubl;
plsqsubinfoLowerLevels = plsqsubinfoResults + 1;
cDepthQueryMaxLowerLevels = cDepthQueryMax - 1;
lserr = QuerySublineCpPpointCore(plssublLowerLevels, cp, cDepthQueryMaxLowerLevels,
plsqsubinfoLowerLevels, &cActualDepthLowerLevels, plstextcell);
if (lserr != lserrNone)
return lserr;
*pcActualDepth = cActualDepthLowerLevels + 1;
ptStartLowerLevels.u = pt.u + lsqout.pointUvStartSubline.u;
ptStartLowerLevels.v = pt.v + lsqout.pointUvStartSubline.v;
TransformPointsOnLowerLevels(plsqsubinfoLowerLevels, cActualDepthLowerLevels, plstextcell,
&ptStartLowerLevels, plssubl->lstflow, plssublLowerLevels->lstflow);
}
return lserrNone;
}
// %%Function: QuerySublinePointPcpCore
// %%Contact: victork
//
/*
* Returns dim-info of the cp in the line, that a) contains given point or
* b) is closest to it from the left or
* c) is just closest to it
*/
LSERR QuerySublinePointPcpCore(
PLSSUBL plssubl,
PCPOINTUV pptIn,
DWORD cDepthQueryMax, /* IN: allocated size of results array */
PLSQSUBINFO plsqsubinfoResults, /* OUT: array[cDepthQueryMax] of results */
DWORD* pcActualDepth, /* OUT: size of results array (filled) */
PLSTEXTCELL plstextcell) /* OUT: Text cell info */
{
PLSC plsc;
LSERR lserr = lserrNone;
PLSDNODE pdn, pdnPrev = NULL;
POINTUV pt, ptInside, ptInsideLocal;
LSCP cpLim;
LSQIN lsqin;
LSQOUT lsqout;
PLSSUBL plssublLowerLevels;
POINTUV ptStartLowerLevels;
PLSQSUBINFO plsqsubinfoLowerLevels;
DWORD cDepthQueryMaxLowerLevels;
DWORD cActualDepthLowerLevels;
long upQuery;
Assert(FIsLSSUBL(plssubl));
Assert(!plssubl->fDupInvalid);
if (cDepthQueryMax == 0)
{
return lserrInsufficientQueryDepth;
}
plsc = plssubl->plsc;
cpLim = plssubl->cpLimDisplay;
pt.u = 0;
pt.v = 0;
pdn = plssubl->plsdnFirst;
/* Skip over autonumbers & starting pens & empty dnodes */
while (FDnodeBeforeCpLim(pdn, cpLim) && (FIsNotInContent(pdn) || !(FIsDnodeReal(pdn)) || FIsZeroWidth(pdn)))
{
pdn = AdvanceToNextDnodeQuery(pdn, &pt);
}
if (!FDnodeBeforeCpLim(pdn, cpLim))
{ /* empty subline */
*pcActualDepth = 0;
return lserrNone;
}
upQuery = pptIn->u;
/*
* Find dnode with our point inside.
*
* We look only at upQuery to do it.
*/
// if pt.u >= upQuery, pdn is the dnode to query, else...
if (pt.u <= upQuery)
{
// skip until the end or dnode to the right of our point
// (That means extra work, but covers zero dup situation without additional if.)
while (FDnodeBeforeCpLim(pdn, cpLim) && pt.u <= upQuery)
{
pdnPrev = pdn;
pdn = AdvanceToNextDnodeQuery(pdn, &pt);
}
if (FIsDnodeBorder(pdnPrev))
{
if (pdnPrev->fOpenBorder)
{
// upQuery was in the previous opening border - pdn is the dnode we need
Assert(FDnodeBeforeCpLim(pdn, cpLim));
}
else
{
// upQuery was in the previous closing border - dnode we need is before the border
pdn = pdnPrev;
Assert(pdn != NULL && !FIsNotInContent(pdn));
pdnPrev = BacktrackToPreviousDnode(pdnPrev, &pt);
pdn = pdnPrev;
Assert(pdn != NULL && !FIsNotInContent(pdn));
pdnPrev = BacktrackToPreviousDnode(pdnPrev, &pt);
}
}
else
{
/* go back to the previous dnode */
pdn = pdnPrev;
pdnPrev = BacktrackToPreviousDnode(pdnPrev, &pt);
// if it is a pen/border or empty dnode (non-req hyphen), skip them all
// (Border cannot be the previous dnode, but is possble later)
while (pdn != NULL && (!(FIsDnodeReal(pdn)) || FIsZeroWidth(pdn)))
{
pdn = pdnPrev;
pdnPrev = BacktrackToPreviousDnode(pdnPrev, &pt);
}
// "nothing good to the left" situation is impossible
Assert(pdn != NULL && !FIsNotInContent(pdn));
}
}
// We have found the leftmost dnode with our dup to the right of it
// pt is just before it, ask method for details
pt.v += pdn->u.real.lschp.dvpPos; // go to the local baseline
PrepareQueryCall(plssubl, pdn, &lsqin);
// get query point relative to the starting point of the dnode
// we give no guarantee that it is really inside dnode box
ptInside.u = pptIn->u - pt.u;
ptInside.v = pptIn->v - pt.v;
lserr = (*plsc->lsiobjcontext.rgobj[pdn->u.real.lschp.idObj].lsim.pfnQueryPointPcp)
(pdn->u.real.pdobj, &ptInside, &lsqin, &lsqout);
if (lserr != lserrNone)
return lserr;
lserr = FillInQueryResults(plsc, plssubl, plsqsubinfoResults, pdn, &pt, &lsqout);
if (lserr != lserrNone)
return lserr;
if (lsqout.plssubl == NULL) // terminal object
{
*pcActualDepth = 1;
FillInTextCellInfo(plsc, pdn, &pt, &lsqout, plstextcell);
}
else // there are more level(s)
{
// recursive call to fill lower levels
plssublLowerLevels = lsqout.plssubl;
plsqsubinfoLowerLevels = plsqsubinfoResults + 1;
cDepthQueryMaxLowerLevels = cDepthQueryMax - 1;
// get query point in lower level subline coordinate system
lserr = LsPointUV2FromPointUV1(plssubl->lstflow, &(lsqout.pointUvStartSubline), &ptInside, /* IN: end input point (TF1) */
plssublLowerLevels->lstflow, &ptInsideLocal);
if (lserr != lserrNone)
return lserr;
lserr = QuerySublinePointPcpCore(plssublLowerLevels, &ptInsideLocal, cDepthQueryMaxLowerLevels,
plsqsubinfoLowerLevels, &cActualDepthLowerLevels, plstextcell);
if (lserr != lserrNone)
return lserr;
*pcActualDepth = cActualDepthLowerLevels + 1;
ptStartLowerLevels.u = pt.u + lsqout.pointUvStartSubline.u;
ptStartLowerLevels.v = pt.v + lsqout.pointUvStartSubline.v;
TransformPointsOnLowerLevels(plsqsubinfoLowerLevels, cActualDepthLowerLevels, plstextcell,
&ptStartLowerLevels, plssubl->lstflow, plssublLowerLevels->lstflow);
}
return lserrNone;
}
// %%Function: PrepareQueryCall
// %%Contact: victork
//
static void PrepareQueryCall(PLSSUBL plssubl, PLSDNODE pdn, LSQIN* plsqin)
{
plsqin->lstflowSubline = plssubl->lstflow;
plsqin->plsrun = pdn->u.real.plsrun;
plsqin->cpFirstRun = pdn->cpFirst;
plsqin->dcpRun = pdn->dcp;
plsqin->heightsPresRun = pdn->u.real.objdim.heightsPres;
plsqin->dupRun = pdn->u.real.dup;
plsqin->dvpPosRun = pdn->u.real.lschp.dvpPos;
}
// %%Function: FillInQueryResults
// %%Contact: victork
//
static LSERR FillInQueryResults(
PLSC plsc,
PLSSUBL plssubl,
PLSQSUBINFO plsqsubinfoResults,
PLSDNODE pdn,
POINTUV* ppt,
LSQOUT* plsqout
)
{
OBJDIM objdimSubline;
LSERR lserr;
PLSDNODE pdnNext, pdnPrev;
// fill in subline info
lserr = LssbGetObjDimSubline(plssubl, &(plsqsubinfoResults->lstflowSubline), &objdimSubline);
if (lserr != lserrNone)
return lserr;
lserr = LssbGetDupSubline(plssubl, &(plsqsubinfoResults->lstflowSubline), &plsqsubinfoResults->dupSubline);
if (lserr != lserrNone)
return lserr;
plsqsubinfoResults->cpFirstSubline = plssubl->cpFirst;
plsqsubinfoResults->dcpSubline = plssubl->cpLimDisplay - plssubl->cpFirst;
plsqsubinfoResults->pointUvStartSubline.u = 0;
plsqsubinfoResults->pointUvStartSubline.v = 0;
plsqsubinfoResults->heightsPresSubline = objdimSubline.heightsPres;
// fill in dnode info
if (IdObjFromDnode(pdn) == IobjTextFromLsc(&(plsc->lsiobjcontext)))
plsqsubinfoResults->idobj = idObjText;
else
plsqsubinfoResults->idobj = pdn->u.real.lschp.idObj;
plsqsubinfoResults->plsrun = pdn->u.real.plsrun;
plsqsubinfoResults->cpFirstRun = pdn->cpFirst;
plsqsubinfoResults->dcpRun = pdn->dcp;
plsqsubinfoResults->pointUvStartRun = *ppt; // local baseline
plsqsubinfoResults->heightsPresRun = pdn->u.real.objdim.heightsPres;
plsqsubinfoResults->dupRun = pdn->u.real.dup;
plsqsubinfoResults->dvpPosRun = pdn->u.real.lschp.dvpPos;
// fill in object info
plsqsubinfoResults->pointUvStartObj.u = ppt->u + plsqout->pointUvStartObj.u;
plsqsubinfoResults->pointUvStartObj.v = ppt->v + plsqout->pointUvStartObj.v;
plsqsubinfoResults->heightsPresObj = plsqout->heightsPresObj;
plsqsubinfoResults->dupObj = plsqout->dupObj;
// add borders info
plsqsubinfoResults->dupBorderAfter = 0;
plsqsubinfoResults->dupBorderBefore = 0;
if (pdn->u.real.lschp.fBorder)
{
pdnNext = pdn->plsdnNext;
if (pdnNext != NULL && FIsDnodeClosingBorder(pdnNext))
{
plsqsubinfoResults->dupBorderAfter = pdnNext->u.pen.dup;
}
pdnPrev = pdn->plsdnPrev;
if (pdnPrev != NULL && FIsDnodeOpenBorder(pdnPrev))
{
Assert(FIsInContent(pdnPrev));
plsqsubinfoResults->dupBorderBefore = pdnPrev->u.pen.dup;
}
}
return lserrNone;
}
// %%Function: FillInTextCellInfo
// %%Contact: victork
//
static void FillInTextCellInfo(
PLSC plsc,
PLSDNODE pdn,
POINTUV* ppt,
LSQOUT* plsqout,
PLSTEXTCELL plstextcell /* OUT: Text cell info */
)
{
if (IdObjFromDnode(pdn) == IobjTextFromLsc(&(plsc->lsiobjcontext)))
{
// text has cell info filled - copy it
*plstextcell = plsqout->lstextcell;
// but starting point is relative to the begining of dnode - adjust to that of subline
plstextcell->pointUvStartCell.u += ppt->u;
plstextcell->pointUvStartCell.v += ppt->v;
// adjust cpEndCell if some hidden text got into last ligature - text is unaware of the issue
if (pdn->cpFirst + pdn->dcp < (LSDCP) pdn->cpLimOriginal &&
(LSDCP) plstextcell->cpEndCell == pdn->cpFirst + pdn->dcp - 1)
{
plstextcell->cpEndCell = pdn->cpLimOriginal - 1;
}
// pointer to the dnode to get details quickly - only query manager knows what PCELLDETAILS is
plstextcell->pCellDetails = (PCELLDETAILS)pdn;
}
else
{
// non-text object should not fill lstxtcell, client should not look into it
// I fill it with object information for debug purposes
// Consider zapping it in lsqline later (Rick's suggestion)
plstextcell->cpStartCell = pdn->cpFirst;
plstextcell->cpEndCell = pdn->cpFirst + pdn->dcp - 1;
plstextcell->pointUvStartCell = *ppt;
plstextcell->dupCell = pdn->u.real.dup;
plstextcell->cCharsInCell = 0;
plstextcell->cGlyphsInCell = 0;
plstextcell->pCellDetails = NULL;
}
}
// %%Function: TransformPointsOnLowerLevels
// %%Contact: victork
//
// transform all vectors in results array from lstflow2 to lstflow1, adding pointuvStart (lstflow1)
static void TransformPointsOnLowerLevels(
PLSQSUBINFO plsqsubinfo, /* IN/OUT: results array */
DWORD cDepth, /* IN: size of results array */
PLSTEXTCELL plstextcell, // IN/OUT: text cell
PPOINTUV ppointuvStart, // IN: in lstflow1
LSTFLOW lstflow1, // IN: lstflow1
LSTFLOW lstflow2) // IN: lstflow2
{
// Have to apply formulas
// VectorOut.u = k11 * VectorIn.u + k12 * VectorIn.v + pointuvStart.u
// VectorOut.v = k21 * VectorIn.u + k22 * VectorIn.v + pointuvStart.u
// to several vectors in results array (all elements in k matrix are zero or +/- 1)
// Algorithm: find the matrix first, then use it
DWORD k[4];
POINTUV pointuv0, pointuv1, pointuv2;
pointuv0.u = 0;
pointuv0.v = 0;
pointuv1.u = 1;
pointuv1.v = 0;
LsPointUV2FromPointUV1(lstflow2, &pointuv0, &pointuv1, lstflow1, &pointuv2);
k[0] = pointuv2.u; // k11
k[1] = pointuv2.v; // k21
pointuv1.u = 0;
pointuv1.v = 1;
LsPointUV2FromPointUV1(lstflow2, &pointuv0, &pointuv1, lstflow1, &pointuv2);
k[2] = pointuv2.u; // k12
k[3] = pointuv2.v; // k22
// all points in lower levels are in lstflowLowerLevels (lstflow2) with starting point at the
// beginning of the top lower levels subline
// Translate them to lstflowTop (lstflow1) and starting point of our subline.
while (cDepth > 0)
{
ApplyFormula(&(plsqsubinfo->pointUvStartSubline), k, ppointuvStart);
ApplyFormula(&(plsqsubinfo->pointUvStartRun), k, ppointuvStart);
ApplyFormula(&(plsqsubinfo->pointUvStartObj), k, ppointuvStart);
plsqsubinfo++;
cDepth--;
}
// StartCell point should be adjusted too
ApplyFormula(&(plstextcell->pointUvStartCell), k, ppointuvStart);
}
// %%Function: ApplyFormula
// %%Contact: victork
//
static void ApplyFormula(PPOINTUV ppointuv, DWORD* rgk, PPOINTUV ppointuvStart)
{
POINTUV pointuvTemp;
pointuvTemp.u = ppointuvStart->u + rgk[0] * ppointuv->u + rgk[2] * ppointuv->v;
pointuvTemp.v = ppointuvStart->v + rgk[1] * ppointuv->u + rgk[3] * ppointuv->v;
*ppointuv = pointuvTemp;
}
// %%Function: AdvanceToNextDnodeQuery
// %%Contact: victork
//
/*
* Advance to the next node and update pen position (never goes into sublines)
*/
static PLSDNODE AdvanceToNextDnodeQuery(PLSDNODE pdn, POINTUV* ppt)
{
if (pdn->klsdn == klsdnReal)
{
ppt->u += pdn->u.real.dup;
}
else /* case klsdnPen */
{
ppt->u += pdn->u.pen.dup;
ppt->v += pdn->u.pen.dvp;
}
return pdn->plsdnNext;
}
// %%Function: BacktrackToPreviousDnode
// %%Contact: victork
//
// Backtrack and downdate pen position.
// Both parameters are input/output
// Input: dnode number N-1 and point at the beginning of the dnode number N
// Output: point at the beginning of the dnode number N-1
// Return: dnode number N-2
static PLSDNODE BacktrackToPreviousDnode(PLSDNODE pdn, POINTUV* ppt)
{
if (FIsDnodeReal(pdn))
{
ppt->u -= pdn->u.real.dup;
}
else /* it's Pen */
{
ppt->u -= pdn->u.pen.dup;
ppt->v -= pdn->u.pen.dvp;
}
return pdn->plsdnPrev;
}