#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; }